Skip to content
Closed
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 @@ -58,8 +58,9 @@
import static org.apache.hadoop.fs.s3a.Constants.AWS_S3_CENTRAL_REGION;
import static org.apache.hadoop.fs.s3a.Constants.EXPERIMENTAL_AWS_INTERNAL_THROTTLING;
import static org.apache.hadoop.fs.s3a.Constants.EXPERIMENTAL_AWS_INTERNAL_THROTTLING_DEFAULT;
import static org.apache.hadoop.fs.s3a.Constants.S3_ENCRYPTION_ALGORITHM;
import static org.apache.hadoop.fs.s3a.Constants.S3_ENCRYPTION_KEY;
import static org.apache.hadoop.fs.s3a.S3AUtils.getEncryptionAlgorithm;
import static org.apache.hadoop.fs.s3a.S3AUtils.getS3EncryptionKey;
import static org.apache.hadoop.fs.s3a.S3AUtils.translateException;

/**
Expand Down Expand Up @@ -96,6 +97,9 @@ public class DefaultS3ClientFactory extends Configured
/** Exactly once log to inform about ignoring the AWS-SDK Warnings for CSE. */
private static final LogExactlyOnce IGNORE_CSE_WARN = new LogExactlyOnce(LOG);

/** Bucket name. */
private String bucket;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

final?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cannot make it final. Assigning it a value in a method


/**
* Create the client by preparing the AwsConf configuration
* and then invoking {@code buildAmazonS3Client()}.
Expand All @@ -105,9 +109,10 @@ public AmazonS3 createS3Client(
final URI uri,
final S3ClientCreationParameters parameters) throws IOException {
Configuration conf = getConf();
bucket = uri.getHost();
final ClientConfiguration awsConf = S3AUtils
.createAwsConf(conf,
uri.getHost(),
bucket,
Constants.AWS_SERVICE_IDENTIFIER_S3);
// add any headers
parameters.getHeaders().forEach((h, v) ->
Expand All @@ -126,10 +131,13 @@ public AmazonS3 createS3Client(
awsConf.setUserAgentSuffix(parameters.getUserAgentSuffix());
}

// Get the encryption method for this bucket.
S3AEncryptionMethods encryptionMethods =
getEncryptionAlgorithm(bucket, conf);
try {
if (S3AEncryptionMethods.getMethod(S3AUtils.
lookupPassword(conf, S3_ENCRYPTION_ALGORITHM, null))
.equals(S3AEncryptionMethods.CSE_KMS)) {
// If CSE is enabled then build a S3EncryptionClient.
if (S3AEncryptionMethods.CSE_KMS.getMethod()
.equals(encryptionMethods.getMethod())) {
return buildAmazonS3EncryptionClient(
awsConf,
parameters);
Expand Down Expand Up @@ -163,12 +171,11 @@ protected AmazonS3 buildAmazonS3EncryptionClient(
new AmazonS3EncryptionClientV2Builder();
Configuration conf = getConf();

//CSE-KMS Method
String kmsKeyId = S3AUtils.lookupPassword(conf,
S3_ENCRYPTION_KEY, null);
// CSE-KMS Method
String kmsKeyId = getS3EncryptionKey(bucket, conf);
// Check if kmsKeyID is not null
Preconditions.checkArgument(kmsKeyId != null, "CSE-KMS method "
+ "requires KMS key ID. Use " + S3_ENCRYPTION_KEY
Preconditions.checkArgument(!StringUtils.isBlank(kmsKeyId), "CSE-KMS "
+ "method requires KMS key ID. Use " + S3_ENCRYPTION_KEY
+ " property to set it. ");

EncryptionMaterialsProvider materialsProvider =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -441,9 +441,8 @@ public void initialize(URI name, Configuration originalConf)
instrumentation = new S3AInstrumentation(uri);
initializeStatisticsBinding();
// If CSE-KMS method is set then CSE is enabled.
isCSEEnabled = S3AUtils.lookupPassword(conf,
Constants.S3_ENCRYPTION_ALGORITHM, "")
.equals(S3AEncryptionMethods.CSE_KMS.getMethod());
isCSEEnabled = S3AEncryptionMethods.CSE_KMS.getMethod()
.equals(getS3EncryptionAlgorithm().getMethod());
LOG.debug("Client Side Encryption enabled: {}", isCSEEnabled);
setCSEGauge();
// Username is the current user at the time the FS was instantiated.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1578,10 +1578,13 @@ static void patchSecurityCredentialProviders(Configuration conf) {
* @return the encryption key or ""
* @throws IllegalArgumentException bad arguments.
*/
@SuppressWarnings("deprecation")
public static String getS3EncryptionKey(String bucket,
Configuration conf) {
try {
return lookupPassword(bucket, conf, Constants.S3_ENCRYPTION_KEY);
String oldKey = lookupPassword(bucket, conf, SERVER_SIDE_ENCRYPTION_KEY);
// return the newKey with oldKey as fallback/default value.
return lookupPassword(bucket, conf, S3_ENCRYPTION_KEY, null, oldKey);
} catch (IOException e) {
LOG.error("Cannot retrieve " + Constants.S3_ENCRYPTION_KEY, e);
return "";
Expand All @@ -1599,11 +1602,19 @@ public static String getS3EncryptionKey(String bucket,
* one is set.
* @throws IOException on any validation problem.
*/
@SuppressWarnings("deprecation")
public static S3AEncryptionMethods getEncryptionAlgorithm(String bucket,
Configuration conf) throws IOException {
// lookup old property and use that as default/fallback for new lookup.
S3AEncryptionMethods oldEncryptionMethod = S3AEncryptionMethods.getMethod(
lookupPassword(bucket, conf,
SERVER_SIDE_ENCRYPTION_ALGORITHM));
// new lookup.
S3AEncryptionMethods encryptionMethod = S3AEncryptionMethods.getMethod(
lookupPassword(bucket, conf,
Constants.S3_ENCRYPTION_ALGORITHM));
Constants.S3_ENCRYPTION_ALGORITHM,
null,
oldEncryptionMethod.getMethod()));
String encryptionKey = getS3EncryptionKey(bucket, conf);
int encryptionKeyLen =
StringUtils.isBlank(encryptionKey) ? 0 : encryptionKey.length();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1426,6 +1426,55 @@ Finally, the public `s3a://landsat-pds/` bucket can be accessed anonymously:
</property>
```

#### per-bucket configuration and deprecated configuration options

Excluding secrets held in JCEKS files, a per-bucket declaration of a
deprecated property will take priority over a global option.


This means that when setting encryption options in XML files,
the option, `fs.bucket.BUCKET.fs.s3a.server-side-encryption-algorithm`
will take priority over the global value of `fs.bucket.s3a.encryption.algorithm`.
The same holds for the encryption key option `fs.s3a.encryption.key`
and its predecessor `fs.s3a.server-side-encryption.key`.


For a site configuration of:

```xml
<property>
<name>fs.s3a.bucket.nightly.server-side-encryption-algorithm</name>
<value>SSE-KMS</value>
</property>

<property>
<name>fs.s3a.bucket.nightly.server-side-encryption.key</name>
<value>arn:aws:kms:eu-west-2:1528130000000:key/753778e4-2d0f-42e6-b894-6a3ae4ea4e5f</value>
</property>

<property>
<name>fs.s3a.encryption.algorithm</name>
<value>AES256</value>
</property>

<property>
<name>fs.s3a.encryption.key</name>
<value>unset</value>
</property>


```

The bucket "nightly" will be encrypted with SSE-KMS using the KMS key
`arn:aws:kms:eu-west-2:1528130000000:key/753778e4-2d0f-42e6-b894-6a3ae4ea4e5f`

This *does not hold* when encryption settings are held in JCEKS stores.
In that situation, *a global declaration using the newer key takes priority
over a per-bucket declaration with the older key name.




### Customizing S3A secrets held in credential files


Expand All @@ -1444,14 +1493,25 @@ fs.s3a.encryption.key
fs.s3a.bucket.nightly.access.key
fs.s3a.bucket.nightly.secret.key
fs.s3a.bucket.nightly.session.token
fs.s3a.bucket.nightly.server-side-encryption.key
fs.s3a.bucket.nightly.server-side-encryption-algorithm
fs.s3a.bucket.nightly.encryption.key
fs.s3a.bucket.nightly.encryption.algorithm
```

When accessing the bucket `s3a://nightly/`, the per-bucket configuration
options for that bucket will be used, here the access keys and token,
and including the encryption algorithm and key.

If the configuration mixes old and new key names, then the new names take
priority over the older values, even if they are not scoped to the bucket

```
fs.s3a.encryption.algorithm
fs.s3a.encryption.key
fs.s3a.bucket.nightly.server-side-encryption-algorithm
fs.s3a.bucket.nightly.server-side-encryption.key
```



### <a name="per_bucket_endpoints"></a>Using Per-Bucket Configuration to access data round the world

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@
import static org.apache.hadoop.fs.s3a.Constants.S3_ENCRYPTION_KEY;
import static org.apache.hadoop.fs.s3a.Constants.SERVER_SIDE_ENCRYPTION_ALGORITHM;
import static org.apache.hadoop.fs.s3a.Constants.SERVER_SIDE_ENCRYPTION_KEY;
import static org.apache.hadoop.fs.s3a.S3ATestUtils.getTestBucketName;
import static org.apache.hadoop.fs.s3a.S3ATestUtils.removeBaseAndBucketOverrides;
import static org.apache.hadoop.fs.s3a.S3ATestUtils.skipIfEncryptionTestsDisabled;
import static org.apache.hadoop.fs.s3a.S3AUtils.getEncryptionAlgorithm;
import static org.apache.hadoop.fs.s3a.S3AUtils.getS3EncryptionKey;

/**
* Test whether or not encryption works by turning it on. Some checks
Expand Down Expand Up @@ -163,8 +165,9 @@ protected String createFilename(String name) {
*/
protected void assertEncrypted(Path path) throws IOException {
//S3 will return full arn of the key, so specify global arn in properties
String kmsKeyArn = this.getConfiguration().
getTrimmed(S3_ENCRYPTION_KEY);
String kmsKeyArn =
getS3EncryptionKey(getTestBucketName(getConfiguration()),
getConfiguration());
S3AEncryptionMethods algorithm = getSSEAlgorithm();
EncryptionTestUtils.assertEncrypted(getFileSystem(),
path,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,12 @@
import static org.apache.hadoop.fs.s3a.Constants.MULTIPART_MIN_SIZE;
import static org.apache.hadoop.fs.s3a.Constants.S3_ENCRYPTION_ALGORITHM;
import static org.apache.hadoop.fs.s3a.Constants.S3_ENCRYPTION_KEY;
import static org.apache.hadoop.fs.s3a.Constants.SERVER_SIDE_ENCRYPTION_ALGORITHM;
import static org.apache.hadoop.fs.s3a.Constants.SERVER_SIDE_ENCRYPTION_KEY;
import static org.apache.hadoop.fs.s3a.S3ATestUtils.assume;
import static org.apache.hadoop.fs.s3a.S3ATestUtils.getTestBucketName;
import static org.apache.hadoop.fs.s3a.S3ATestUtils.getTestPropertyBool;
import static org.apache.hadoop.fs.s3a.S3ATestUtils.removeBaseAndBucketOverrides;
import static org.apache.hadoop.test.LambdaTestUtils.intercept;

/**
Expand Down Expand Up @@ -193,6 +197,7 @@ KEY_SCALE_TESTS_ENABLED, getTestPropertyBool(
* Testing how unencrypted and encrypted data behaves when read through
* CSE enabled and disabled FS respectively.
*/
@SuppressWarnings("deprecation")
@Test
public void testEncryptionEnabledAndDisabledFS() throws Exception {
maybeSkipTest();
Expand All @@ -203,8 +208,12 @@ public void testEncryptionEnabledAndDisabledFS() throws Exception {
Path encryptedFilePath = path(getMethodName() + "cse");

// Initialize a CSE disabled FS.
cseDisabledConf.unset(S3_ENCRYPTION_ALGORITHM);
cseDisabledConf.unset(S3_ENCRYPTION_KEY);
removeBaseAndBucketOverrides(getTestBucketName(cseDisabledConf),
cseDisabledConf,
S3_ENCRYPTION_ALGORITHM,
S3_ENCRYPTION_KEY,
SERVER_SIDE_ENCRYPTION_ALGORITHM,
SERVER_SIDE_ENCRYPTION_KEY);
cseDisabledFS.initialize(getFileSystem().getUri(),
cseDisabledConf);

Expand Down Expand Up @@ -288,7 +297,7 @@ protected void validateEncryptionForFileSize(int len) throws IOException {
/**
* Skip tests if certain conditions are met.
*/
protected abstract void maybeSkipTest();
protected abstract void maybeSkipTest() throws IOException;

/**
* Assert that at path references an encrypted blob.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.s3a.impl.HeaderProcessing;

import static org.apache.hadoop.fs.s3a.S3ATestUtils.getTestBucketName;
import static org.apache.hadoop.fs.s3a.S3ATestUtils.skipIfEncryptionNotSet;
import static org.apache.hadoop.fs.s3a.S3ATestUtils.skipIfEncryptionTestsDisabled;
import static org.apache.hadoop.fs.s3a.S3AUtils.getS3EncryptionKey;

/**
* Testing the S3 CSE - KMS method.
Expand All @@ -53,7 +55,7 @@ protected Configuration createConfiguration() {
}

@Override
protected void maybeSkipTest() {
protected void maybeSkipTest() throws IOException {
skipIfEncryptionTestsDisabled(getConfiguration());
// skip the test if CSE-KMS or KMS key is not set.
skipIfEncryptionNotSet(getConfiguration(), S3AEncryptionMethods.CSE_KMS);
Expand All @@ -71,8 +73,8 @@ protected void assertEncrypted(Path path) throws IOException {

// Assert content encryption algo for KMS, is present in the
// materials description and KMS key ID isn't.
String keyId =
getConfiguration().get(Constants.S3_ENCRYPTION_KEY);
String keyId = getS3EncryptionKey(getTestBucketName(getConfiguration()),
getConfiguration());
Assertions.assertThat(processHeader(fsXAttrs,
xAttrPrefix + Headers.MATERIALS_DESCRIPTION))
.describedAs("Materials Description should contain the content "
Expand Down
Loading