Skip to content

Commit c2596c7

Browse files
Move new S3A encryption methods to a new class S3AEncryption
Use a single ITest class for encryption context with parameterized encryption algorithm and kms key
1 parent 71ffcd5 commit c2596c7

11 files changed

Lines changed: 301 additions & 273 deletions

hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/S3AUtils.java

Lines changed: 5 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818

1919
package org.apache.hadoop.fs.s3a;
2020

21-
import com.fasterxml.jackson.databind.ObjectMapper;
2221
import software.amazon.awssdk.awscore.exception.AwsServiceException;
2322
import software.amazon.awssdk.core.exception.AbortedException;
2423
import software.amazon.awssdk.core.exception.ApiCallAttemptTimeoutException;
@@ -28,7 +27,6 @@
2827
import software.amazon.awssdk.services.s3.model.S3Exception;
2928
import software.amazon.awssdk.services.s3.model.S3Object;
3029

31-
import org.apache.commons.codec.binary.Base64;
3230
import org.apache.commons.lang3.StringUtils;
3331
import org.apache.hadoop.classification.InterfaceAudience;
3432
import org.apache.hadoop.classification.InterfaceStability;
@@ -39,6 +37,7 @@
3937
import org.apache.hadoop.fs.PathFilter;
4038
import org.apache.hadoop.fs.PathIOException;
4139
import org.apache.hadoop.fs.RemoteIterator;
40+
import org.apache.hadoop.fs.s3a.impl.S3AEncryption;
4241
import org.apache.hadoop.util.functional.RemoteIterators;
4342
import org.apache.hadoop.fs.s3a.auth.delegation.EncryptionSecrets;
4443
import org.apache.hadoop.fs.s3a.impl.MultiObjectDeleteException;
@@ -64,7 +63,6 @@
6463
import java.lang.reflect.Modifier;
6564
import java.net.SocketTimeoutException;
6665
import java.net.URI;
67-
import java.nio.charset.StandardCharsets;
6866
import java.nio.file.AccessDeniedException;
6967
import java.util.ArrayList;
7068
import java.util.Collection;
@@ -127,7 +125,6 @@ public final class S3AUtils {
127125
S3AEncryptionMethods.SSE_S3.getMethod()
128126
+ " is enabled but an encryption key was set in "
129127
+ Constants.S3_ENCRYPTION_KEY;
130-
131128
public static final String EOF_MESSAGE_IN_XML_PARSER
132129
= "Failed to sanitize XML document destined for handler class";
133130

@@ -1316,7 +1313,7 @@ static void patchSecurityCredentialProviders(Configuration conf) {
13161313
* @throws IOException on any IO problem
13171314
* @throws IllegalArgumentException bad arguments
13181315
*/
1319-
private static String lookupBucketSecret(
1316+
public static String lookupBucketSecret(
13201317
String bucket,
13211318
Configuration conf,
13221319
String baseKey)
@@ -1406,79 +1403,6 @@ public static String getS3EncryptionKey(
14061403
}
14071404
}
14081405

1409-
/**
1410-
* Get any SSE context, without propagating exceptions from
1411-
* JCEKs files.
1412-
* @param bucket bucket to query for
1413-
* @param conf configuration to examine
1414-
* @return the encryption context value or ""
1415-
* @throws IllegalArgumentException bad arguments.
1416-
*/
1417-
public static String getS3EncryptionContext(
1418-
String bucket,
1419-
Configuration conf) {
1420-
try {
1421-
return getEncryptionContextValue(bucket, conf);
1422-
} catch (IOException e) {
1423-
// never going to happen, but to make sure, covert to
1424-
// runtime exception
1425-
throw new UncheckedIOException(e);
1426-
}
1427-
}
1428-
1429-
/**
1430-
* Get any SSE context from a configuration/credential provider.
1431-
* This includes converting the values to a base64-encoded UTF-8 string
1432-
* holding JSON with the encryption context key-value pairs
1433-
* @param bucket bucket to query for
1434-
* @param conf configuration to examine
1435-
* @param propagateExceptions should IO exceptions be rethrown?
1436-
* @return the Base64 encryption context or ""
1437-
* @throws IllegalArgumentException bad arguments.
1438-
* @throws IOException if propagateExceptions==true and reading a JCEKS file raised an IOE
1439-
*/
1440-
public static String getS3EncryptionContextBase64Encoded(
1441-
String bucket,
1442-
Configuration conf,
1443-
boolean propagateExceptions) throws IOException {
1444-
try {
1445-
final String encryptionContextValue = getEncryptionContextValue(bucket, conf);
1446-
if (StringUtils.isBlank(encryptionContextValue)) {
1447-
return "";
1448-
}
1449-
final Map<String, String> encryptionContextMap = getTrimmedStringCollectionSplitByEquals(
1450-
encryptionContextValue);
1451-
if (encryptionContextMap.isEmpty()) {
1452-
return "";
1453-
}
1454-
final String encryptionContextJson = new ObjectMapper().writeValueAsString(
1455-
encryptionContextMap);
1456-
return Base64.encodeBase64String(encryptionContextJson.getBytes(StandardCharsets.UTF_8));
1457-
} catch (IOException e) {
1458-
if (propagateExceptions) {
1459-
throw e;
1460-
}
1461-
LOG.warn("Cannot retrieve {} for bucket {}",
1462-
S3_ENCRYPTION_CONTEXT, bucket, e);
1463-
return "";
1464-
}
1465-
}
1466-
1467-
private static String getEncryptionContextValue(String bucket, Configuration conf)
1468-
throws IOException {
1469-
// look up the per-bucket value of the encryption context
1470-
String encryptionContext = lookupBucketSecret(bucket, conf, S3_ENCRYPTION_CONTEXT);
1471-
if (encryptionContext == null) {
1472-
// look up the global value of the encryption context
1473-
encryptionContext = lookupPassword(null, conf, S3_ENCRYPTION_CONTEXT);
1474-
}
1475-
if (encryptionContext == null) {
1476-
// no encryption context, return ""
1477-
return "";
1478-
}
1479-
return encryptionContext;
1480-
}
1481-
14821406
/**
14831407
* Get the server-side encryption or client side encryption algorithm.
14841408
* This includes validation of the configuration, checking the state of
@@ -1535,6 +1459,8 @@ public static EncryptionSecrets buildEncryptionSecrets(String bucket,
15351459
int encryptionKeyLen =
15361460
StringUtils.isBlank(encryptionKey) ? 0 : encryptionKey.length();
15371461
String diagnostics = passwordDiagnostics(encryptionKey, "key");
1462+
String encryptionContext = S3AEncryption.getS3EncryptionContextBase64Encoded(bucket, conf,
1463+
encryptionMethod.requiresSecret());
15381464
switch (encryptionMethod) {
15391465
case SSE_C:
15401466
LOG.debug("Using SSE-C with {}", diagnostics);
@@ -1570,9 +1496,6 @@ public static EncryptionSecrets buildEncryptionSecrets(String bucket,
15701496
LOG.debug("Data is unencrypted");
15711497
break;
15721498
}
1573-
1574-
String encryptionContext = getS3EncryptionContextBase64Encoded(bucket, conf,
1575-
encryptionMethod.requiresSecret());
15761499
return new EncryptionSecrets(encryptionMethod, encryptionKey, encryptionContext);
15771500
}
15781501

@@ -1779,7 +1702,7 @@ public static Map<String, String> getTrimmedStringCollectionSplitByEquals(
17791702
* @return property value as a <code>Map</code> of <code>String</code>s, or empty
17801703
* <code>Map</code>.
17811704
*/
1782-
private static Map<String, String> getTrimmedStringCollectionSplitByEquals(
1705+
public static Map<String, String> getTrimmedStringCollectionSplitByEquals(
17831706
final String valueString) {
17841707
if (null == valueString) {
17851708
return new HashMap<>();

hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/auth/delegation/EncryptionSecretOperations.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ public static Optional<String> getSSEAwsKMSKey(final EncryptionSecrets secrets)
7171
public static Optional<String> getSSEAwsKMSEncryptionContext(final EncryptionSecrets secrets) {
7272
if ((secrets.getEncryptionMethod() == S3AEncryptionMethods.SSE_KMS
7373
|| secrets.getEncryptionMethod() == S3AEncryptionMethods.DSSE_KMS)
74-
&& secrets.hasEncryptionKey()
7574
&& secrets.hasEncryptionContext()) {
7675
return Optional.of(secrets.getEncryptionContext());
7776
} else {

hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/impl/RequestFactoryImpl.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,8 @@ protected void copyEncryptionParameters(HeadObjectResponse srcom,
270270
LOG.debug("Propagating SSE-KMS settings from source {}",
271271
sourceKMSId);
272272
copyObjectRequestBuilder.ssekmsKeyId(sourceKMSId);
273+
EncryptionSecretOperations.getSSEAwsKMSEncryptionContext(encryptionSecrets)
274+
.ifPresent(copyObjectRequestBuilder::ssekmsEncryptionContext);
273275
return;
274276
}
275277

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package org.apache.hadoop.fs.s3a.impl;
20+
21+
import java.io.IOException;
22+
import java.nio.charset.StandardCharsets;
23+
import java.util.Map;
24+
25+
import com.fasterxml.jackson.databind.ObjectMapper;
26+
import org.slf4j.Logger;
27+
import org.slf4j.LoggerFactory;
28+
29+
import org.apache.commons.codec.binary.Base64;
30+
import org.apache.commons.lang3.StringUtils;
31+
import org.apache.hadoop.conf.Configuration;
32+
import org.apache.hadoop.fs.s3a.S3AUtils;
33+
34+
import static org.apache.hadoop.fs.s3a.Constants.S3_ENCRYPTION_CONTEXT;
35+
36+
/**
37+
* Utility methods for S3A encryption properties.
38+
*/
39+
public final class S3AEncryption {
40+
41+
private static final Logger LOG = LoggerFactory.getLogger(S3AEncryption.class);
42+
43+
private S3AEncryption() {
44+
}
45+
46+
/**
47+
* Get any SSE context from a configuration/credential provider.
48+
* @param bucket bucket to query for
49+
* @param conf configuration to examine
50+
* @return the encryption context value or ""
51+
* @throws IllegalArgumentException bad arguments.
52+
*/
53+
public static String getS3EncryptionContext(String bucket, Configuration conf)
54+
throws IOException {
55+
// look up the per-bucket value of the encryption context
56+
String encryptionContext = S3AUtils.lookupBucketSecret(bucket, conf, S3_ENCRYPTION_CONTEXT);
57+
if (encryptionContext == null) {
58+
// look up the global value of the encryption context
59+
encryptionContext = S3AUtils.lookupPassword(null, conf, S3_ENCRYPTION_CONTEXT);
60+
}
61+
if (encryptionContext == null) {
62+
// no encryption context, return ""
63+
return "";
64+
}
65+
return encryptionContext;
66+
}
67+
68+
/**
69+
* Get any SSE context from a configuration/credential provider.
70+
* This includes converting the values to a base64-encoded UTF-8 string
71+
* holding JSON with the encryption context key-value pairs
72+
* @param bucket bucket to query for
73+
* @param conf configuration to examine
74+
* @param propagateExceptions should IO exceptions be rethrown?
75+
* @return the Base64 encryption context or ""
76+
* @throws IllegalArgumentException bad arguments.
77+
* @throws IOException if propagateExceptions==true and reading a JCEKS file raised an IOE
78+
*/
79+
public static String getS3EncryptionContextBase64Encoded(
80+
String bucket,
81+
Configuration conf,
82+
boolean propagateExceptions) throws IOException {
83+
try {
84+
final String encryptionContextValue = getS3EncryptionContext(bucket, conf);
85+
if (StringUtils.isBlank(encryptionContextValue)) {
86+
return "";
87+
}
88+
final Map<String, String> encryptionContextMap = S3AUtils
89+
.getTrimmedStringCollectionSplitByEquals(encryptionContextValue);
90+
if (encryptionContextMap.isEmpty()) {
91+
return "";
92+
}
93+
final String encryptionContextJson = new ObjectMapper().writeValueAsString(
94+
encryptionContextMap);
95+
return Base64.encodeBase64String(encryptionContextJson.getBytes(StandardCharsets.UTF_8));
96+
} catch (IOException e) {
97+
if (propagateExceptions) {
98+
throw e;
99+
}
100+
LOG.warn("Cannot retrieve {} for bucket {}",
101+
S3_ENCRYPTION_CONTEXT, bucket, e);
102+
return "";
103+
}
104+
}
105+
}

hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/ITestS3AEncryptionDSSEKMSUserDefinedKeyWithEncryptionContext.java

Lines changed: 0 additions & 70 deletions
This file was deleted.

0 commit comments

Comments
 (0)