Skip to content

Commit 020adaa

Browse files
authored
HBASE-29643: Admin API to trigger for System Key rotation (#7394)
1 parent 4e73e8a commit 020adaa

File tree

39 files changed

+1259
-397
lines changed

39 files changed

+1259
-397
lines changed

.rubocop.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,12 @@ Layout/LineLength:
99

1010
Metrics/MethodLength:
1111
Max: 75
12+
13+
GlobalVars:
14+
AllowedVariables:
15+
- $CUST1_ENCODED
16+
- $CUST1_ALIAS
17+
- $CUST1_ENCODED
18+
- $GLOB_CUST_ENCODED
19+
- $TEST
20+
- $TEST_CLUSTER

hbase-client/src/main/java/org/apache/hadoop/hbase/client/Admin.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2664,4 +2664,10 @@ List<LogEntry> getLogEntries(Set<ServerName> serverNames, String logType, Server
26642664

26652665
@InterfaceAudience.Private
26662666
void restoreBackupSystemTable(String snapshotName) throws IOException;
2667+
2668+
/**
2669+
* Refresh the system key cache on all specified region servers.
2670+
* @param regionServers the list of region servers to refresh the system key cache on
2671+
*/
2672+
void refreshSystemKeyCacheOnAllServers(Set<ServerName> regionServers) throws IOException;
26672673
}

hbase-client/src/main/java/org/apache/hadoop/hbase/client/AdminOverAsyncAdmin.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1146,4 +1146,9 @@ public List<String> getCachedFilesList(ServerName serverName) throws IOException
11461146
public void restoreBackupSystemTable(String snapshotName) throws IOException {
11471147
get(admin.restoreBackupSystemTable(snapshotName));
11481148
}
1149+
1150+
@Override
1151+
public void refreshSystemKeyCacheOnAllServers(Set<ServerName> regionServers) throws IOException {
1152+
get(admin.refreshSystemKeyCacheOnAllServers(regionServers));
1153+
}
11491154
}

hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncAdmin.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1874,4 +1874,10 @@ CompletableFuture<List<LogEntry>> getLogEntries(Set<ServerName> serverNames, Str
18741874

18751875
@InterfaceAudience.Private
18761876
CompletableFuture<Void> restoreBackupSystemTable(String snapshotName);
1877+
1878+
/**
1879+
* Refresh the system key cache on all specified region servers.
1880+
* @param regionServers the list of region servers to refresh the system key cache on
1881+
*/
1882+
CompletableFuture<Void> refreshSystemKeyCacheOnAllServers(Set<ServerName> regionServers);
18771883
}

hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncHBaseAdmin.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,11 @@ public CompletableFuture<Void> updateConfiguration(String groupName) {
686686
return wrap(rawAdmin.updateConfiguration(groupName));
687687
}
688688

689+
@Override
690+
public CompletableFuture<Void> refreshSystemKeyCacheOnAllServers(Set<ServerName> regionServers) {
691+
return wrap(rawAdmin.refreshSystemKeyCacheOnAllServers(regionServers));
692+
}
693+
689694
@Override
690695
public CompletableFuture<Void> rollWALWriter(ServerName serverName) {
691696
return wrap(rawAdmin.rollWALWriter(serverName));

hbase-client/src/main/java/org/apache/hadoop/hbase/client/RawAsyncHBaseAdmin.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@
150150
import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.UpdateConfigurationRequest;
151151
import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.UpdateConfigurationResponse;
152152
import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos;
153+
import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.EmptyMsg;
153154
import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.LastHighestWalFilenum;
154155
import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.NameStringPair;
155156
import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.ProcedureDescription;
@@ -4662,4 +4663,28 @@ MasterProtos.RestoreBackupSystemTableResponse> procedureCall(request,
46624663
MasterProtos.RestoreBackupSystemTableResponse::getProcId,
46634664
new RestoreBackupSystemTableProcedureBiConsumer());
46644665
}
4666+
4667+
@Override
4668+
public CompletableFuture<Void> refreshSystemKeyCacheOnAllServers(Set<ServerName> regionServers) {
4669+
CompletableFuture<Void> future = new CompletableFuture<>();
4670+
List<CompletableFuture<Void>> futures =
4671+
regionServers.stream().map(this::refreshSystemKeyCache).collect(Collectors.toList());
4672+
addListener(CompletableFuture.allOf(futures.toArray(new CompletableFuture<?>[0])),
4673+
(result, err) -> {
4674+
if (err != null) {
4675+
future.completeExceptionally(err);
4676+
} else {
4677+
future.complete(result);
4678+
}
4679+
});
4680+
return future;
4681+
}
4682+
4683+
private CompletableFuture<Void> refreshSystemKeyCache(ServerName serverName) {
4684+
return this.<Void> newAdminCaller()
4685+
.action((controller, stub) -> this.<EmptyMsg, EmptyMsg, Void> adminCall(controller, stub,
4686+
EmptyMsg.getDefaultInstance(),
4687+
(s, c, req, done) -> s.refreshSystemKeyCache(controller, req, done), resp -> null))
4688+
.serverName(serverName).call();
4689+
}
46654690
}

hbase-client/src/main/java/org/apache/hadoop/hbase/keymeta/KeymetaAdminClient.java

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,18 @@
2525
import org.apache.hadoop.hbase.io.crypto.ManagedKeyData;
2626
import org.apache.hadoop.hbase.io.crypto.ManagedKeyState;
2727
import org.apache.hadoop.hbase.protobuf.generated.ManagedKeysProtos;
28-
import org.apache.hadoop.hbase.protobuf.generated.ManagedKeysProtos.ManagedKeysRequest;
29-
import org.apache.hadoop.hbase.protobuf.generated.ManagedKeysProtos.ManagedKeysResponse;
28+
import org.apache.hadoop.hbase.protobuf.generated.ManagedKeysProtos.ManagedKeyRequest;
29+
import org.apache.hadoop.hbase.protobuf.generated.ManagedKeysProtos.ManagedKeyResponse;
3030
import org.apache.yetus.audience.InterfaceAudience;
31-
import org.slf4j.Logger;
32-
import org.slf4j.LoggerFactory;
3331

32+
import org.apache.hbase.thirdparty.com.google.protobuf.ByteString;
3433
import org.apache.hbase.thirdparty.com.google.protobuf.ServiceException;
3534

3635
import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
36+
import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.EmptyMsg;
3737

3838
@InterfaceAudience.Public
3939
public class KeymetaAdminClient implements KeymetaAdmin {
40-
private static final Logger LOG = LoggerFactory.getLogger(KeymetaAdminClient.class);
4140
private ManagedKeysProtos.ManagedKeysService.BlockingInterface stub;
4241

4342
public KeymetaAdminClient(Connection conn) throws IOException {
@@ -46,38 +45,54 @@ public KeymetaAdminClient(Connection conn) throws IOException {
4645
}
4746

4847
@Override
49-
public List<ManagedKeyData> enableKeyManagement(String keyCust, String keyNamespace)
48+
public ManagedKeyData enableKeyManagement(byte[] keyCust, String keyNamespace)
5049
throws IOException {
5150
try {
52-
ManagedKeysProtos.GetManagedKeysResponse response = stub.enableKeyManagement(null,
53-
ManagedKeysRequest.newBuilder().setKeyCust(keyCust).setKeyNamespace(keyNamespace).build());
54-
return generateKeyDataList(response);
51+
ManagedKeysProtos.ManagedKeyResponse response =
52+
stub.enableKeyManagement(null, ManagedKeyRequest.newBuilder()
53+
.setKeyCust(ByteString.copyFrom(keyCust)).setKeyNamespace(keyNamespace).build());
54+
return generateKeyData(response);
5555
} catch (ServiceException e) {
5656
throw ProtobufUtil.handleRemoteException(e);
5757
}
5858
}
5959

6060
@Override
61-
public List<ManagedKeyData> getManagedKeys(String keyCust, String keyNamespace)
61+
public List<ManagedKeyData> getManagedKeys(byte[] keyCust, String keyNamespace)
6262
throws IOException, KeyException {
6363
try {
64-
ManagedKeysProtos.GetManagedKeysResponse statusResponse = stub.getManagedKeys(null,
65-
ManagedKeysRequest.newBuilder().setKeyCust(keyCust).setKeyNamespace(keyNamespace).build());
64+
ManagedKeysProtos.GetManagedKeysResponse statusResponse =
65+
stub.getManagedKeys(null, ManagedKeyRequest.newBuilder()
66+
.setKeyCust(ByteString.copyFrom(keyCust)).setKeyNamespace(keyNamespace).build());
6667
return generateKeyDataList(statusResponse);
6768
} catch (ServiceException e) {
6869
throw ProtobufUtil.handleRemoteException(e);
6970
}
7071
}
7172

73+
@Override
74+
public boolean rotateSTK() throws IOException {
75+
try {
76+
ManagedKeysProtos.RotateSTKResponse response =
77+
stub.rotateSTK(null, EmptyMsg.getDefaultInstance());
78+
return response.getRotated();
79+
} catch (ServiceException e) {
80+
throw ProtobufUtil.handleRemoteException(e);
81+
}
82+
}
83+
7284
private static List<ManagedKeyData>
7385
generateKeyDataList(ManagedKeysProtos.GetManagedKeysResponse stateResponse) {
7486
List<ManagedKeyData> keyStates = new ArrayList<>();
75-
for (ManagedKeysResponse state : stateResponse.getStateList()) {
76-
keyStates
77-
.add(new ManagedKeyData(state.getKeyCustBytes().toByteArray(), state.getKeyNamespace(),
78-
null, ManagedKeyState.forValue((byte) state.getKeyState().getNumber()),
79-
state.getKeyMetadata(), state.getRefreshTimestamp()));
87+
for (ManagedKeyResponse state : stateResponse.getStateList()) {
88+
keyStates.add(generateKeyData(state));
8089
}
8190
return keyStates;
8291
}
92+
93+
private static ManagedKeyData generateKeyData(ManagedKeysProtos.ManagedKeyResponse response) {
94+
return new ManagedKeyData(response.getKeyCust().toByteArray(), response.getKeyNamespace(), null,
95+
ManagedKeyState.forValue((byte) response.getKeyState().getNumber()),
96+
response.getKeyMetadata(), response.getRefreshTimestamp());
97+
}
8398
}

hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/Encryption.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -506,12 +506,11 @@ public static void decryptWithSubjectKey(OutputStream out, InputStream in, int o
506506
// is configured
507507
String alternateAlgorithm = conf.get(HConstants.CRYPTO_ALTERNATE_KEY_ALGORITHM_CONF_KEY);
508508
if (alternateAlgorithm != null) {
509-
if (LOG.isDebugEnabled()) {
510-
LOG.debug("Unable to decrypt data with current cipher algorithm '"
511-
+ conf.get(HConstants.CRYPTO_KEY_ALGORITHM_CONF_KEY, HConstants.CIPHER_AES)
512-
+ "'. Trying with the alternate cipher algorithm '" + alternateAlgorithm
513-
+ "' configured.");
514-
}
509+
LOG.debug(
510+
"Unable to decrypt data with current cipher algorithm '{}'. "
511+
+ "Trying with the alternate cipher algorithm '{}' configured.",
512+
conf.get(HConstants.CRYPTO_KEY_ALGORITHM_CONF_KEY, HConstants.CIPHER_AES),
513+
alternateAlgorithm);
515514
Cipher alterCipher = Encryption.getCipher(conf, alternateAlgorithm);
516515
if (alterCipher == null) {
517516
throw new RuntimeException("Cipher '" + alternateAlgorithm + "' not available");
@@ -575,7 +574,7 @@ private static Object createProvider(final Configuration conf, String classNameK
575574
throw new RuntimeException(e);
576575
}
577576
keyProviderCache.put(providerCacheKey, provider);
578-
LOG.debug("Installed " + providerClassName + " into key provider cache");
577+
LOG.debug("Installed {} into key provider cache", providerClassName);
579578
}
580579
return provider;
581580
}

hbase-common/src/main/java/org/apache/hadoop/hbase/keymeta/KeymetaAdmin.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,23 +31,31 @@
3131
public interface KeymetaAdmin {
3232
/**
3333
* Enables key management for the specified custodian and namespace.
34-
* @param keyCust The key custodian in base64 encoded format.
34+
* @param keyCust The key custodian identifier.
3535
* @param keyNamespace The namespace for the key management.
3636
* @return The list of {@link ManagedKeyData} objects each identifying the key and its current
3737
* status.
3838
* @throws IOException if an error occurs while enabling key management.
3939
*/
40-
List<ManagedKeyData> enableKeyManagement(String keyCust, String keyNamespace)
40+
ManagedKeyData enableKeyManagement(byte[] keyCust, String keyNamespace)
4141
throws IOException, KeyException;
4242

4343
/**
4444
* Get the status of all the keys for the specified custodian.
45-
* @param keyCust The key custodian in base64 encoded format.
45+
* @param keyCust The key custodian identifier.
4646
* @param keyNamespace The namespace for the key management.
4747
* @return The list of {@link ManagedKeyData} objects each identifying the key and its current
4848
* status.
4949
* @throws IOException if an error occurs while enabling key management.
5050
*/
51-
List<ManagedKeyData> getManagedKeys(String keyCust, String keyNamespace)
51+
List<ManagedKeyData> getManagedKeys(byte[] keyCust, String keyNamespace)
5252
throws IOException, KeyException;
53+
54+
/**
55+
* Triggers rotation of the System Key (STK) by checking for a new key and propagating it to all
56+
* region servers.
57+
* @return true if a new STK was found and rotated, false if no change was detected
58+
* @throws IOException if an error occurs while rotating the STK
59+
*/
60+
boolean rotateSTK() throws IOException;
5361
}

hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/MockManagedKeyProvider.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,18 +44,27 @@ public class MockManagedKeyProvider extends MockAesKeyProvider implements Manage
4444
private Map<String, ManagedKeyState> keyState = new HashMap<>();
4545
private String systemKeyAlias = "default_system_key_alias";
4646

47+
private boolean shouldThrowExceptionOnGetSystemKey = false;
48+
private boolean shouldThrowExceptionOnGetManagedKey = false;
49+
4750
@Override
4851
public void initConfig(Configuration conf, String providerParameters) {
4952
super.init(providerParameters);
5053
}
5154

5255
@Override
5356
public ManagedKeyData getSystemKey(byte[] systemId) throws IOException {
57+
if (shouldThrowExceptionOnGetSystemKey) {
58+
throw new IOException("Test exception on getSystemKey");
59+
}
5460
return getKey(systemId, systemKeyAlias, ManagedKeyData.KEY_SPACE_GLOBAL);
5561
}
5662

5763
@Override
5864
public ManagedKeyData getManagedKey(byte[] key_cust, String key_namespace) throws IOException {
65+
if (shouldThrowExceptionOnGetManagedKey) {
66+
throw new IOException("Test exception on getManagedKey");
67+
}
5968
String alias = Bytes.toString(key_cust);
6069
return getKey(key_cust, alias, key_namespace);
6170
}
@@ -118,6 +127,14 @@ public String getSystemKeyAlias() {
118127
return this.systemKeyAlias;
119128
}
120129

130+
public void setShouldThrowExceptionOnGetSystemKey(boolean shouldThrowExceptionOnGetSystemKey) {
131+
this.shouldThrowExceptionOnGetSystemKey = shouldThrowExceptionOnGetSystemKey;
132+
}
133+
134+
public void setShouldThrowExceptionOnGetManagedKey(boolean shouldThrowExceptionOnGetManagedKey) {
135+
this.shouldThrowExceptionOnGetManagedKey = shouldThrowExceptionOnGetManagedKey;
136+
}
137+
121138
/**
122139
* Generate a new secret key.
123140
* @return the key

0 commit comments

Comments
 (0)