diff --git a/hadoop-common-project/hadoop-common/src/main/resources/core-default.xml b/hadoop-common-project/hadoop-common/src/main/resources/core-default.xml index 3e9beb9aad..0b8de17c89 100644 --- a/hadoop-common-project/hadoop-common/src/main/resources/core-default.xml +++ b/hadoop-common-project/hadoop-common/src/main/resources/core-default.xml @@ -1623,6 +1623,27 @@ + + fs.s3a.s3guard.ddb.table.sse.enabled + false + + Whether server-side encryption (SSE) is enabled or disabled on the table. + By default it's disabled, meaning SSE is set to AWS owned CMK. + + + + + fs.s3a.s3guard.ddb.table.sse.cmk + + + The KMS Customer Master Key (CMK) used for the KMS encryption on the table. + To specify a CMK, this config value can be its key ID, Amazon Resource Name + (ARN), alias name, or alias ARN. Users only need to provide this config if + the key is different from the default DynamoDB KMS Master Key, which is + alias/aws/dynamodb. + + + fs.s3a.s3guard.ddb.max.retries 9 diff --git a/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/Constants.java b/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/Constants.java index 8593538f96..3e4de05726 100644 --- a/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/Constants.java +++ b/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/Constants.java @@ -568,6 +568,25 @@ private Constants() { */ public static final long S3GUARD_DDB_TABLE_CAPACITY_WRITE_DEFAULT = 0; + /** + * Whether server-side encryption (SSE) is enabled or disabled on the table. + * By default it's disabled, meaning SSE is set to AWS owned CMK. + * @see com.amazonaws.services.dynamodbv2.model.SSESpecification#setEnabled + */ + public static final String S3GUARD_DDB_TABLE_SSE_ENABLED = + "fs.s3a.s3guard.ddb.table.sse.enabled"; + + /** + * The KMS Master Key (CMK) used for the KMS encryption on the table. + * + * To specify a CMK, this config value can be its key ID, Amazon Resource + * Name (ARN), alias name, or alias ARN. Users only provide this config + * if the key is different from the default DynamoDB KMS Master Key, which is + * alias/aws/dynamodb. + */ + public static final String S3GUARD_DDB_TABLE_SSE_CMK = + "fs.s3a.s3guard.ddb.table.sse.cmk"; + /** * The maximum put or delete requests per BatchWriteItem request. * diff --git a/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/s3guard/DynamoDBMetadataStore.java b/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/s3guard/DynamoDBMetadataStore.java index 1ea82dfae3..2805a1bc93 100644 --- a/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/s3guard/DynamoDBMetadataStore.java +++ b/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/s3guard/DynamoDBMetadataStore.java @@ -1870,7 +1870,9 @@ public Map getDiagnostics() throws IOException { throughput.getWriteCapacityUnits() == 0 ? BILLING_MODE_PER_REQUEST : BILLING_MODE_PROVISIONED); - map.put(TABLE, desc.toString()); + map.put("sse", desc.getSSEDescription() == null + ? "DISABLED" + : desc.getSSEDescription().toString()); map.put(MetadataStoreCapabilities.PERSISTS_AUTHORITATIVE_BIT, Boolean.toString(true)); } else { diff --git a/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/s3guard/DynamoDBMetadataStoreTableManager.java b/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/s3guard/DynamoDBMetadataStoreTableManager.java index fe08ecd92a..6383f001a3 100644 --- a/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/s3guard/DynamoDBMetadataStoreTableManager.java +++ b/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/s3guard/DynamoDBMetadataStoreTableManager.java @@ -44,6 +44,7 @@ import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughputDescription; import com.amazonaws.services.dynamodbv2.model.ResourceInUseException; import com.amazonaws.services.dynamodbv2.model.ResourceNotFoundException; +import com.amazonaws.services.dynamodbv2.model.SSESpecification; import com.amazonaws.services.dynamodbv2.model.ScanRequest; import com.amazonaws.services.dynamodbv2.model.ScanResult; import com.amazonaws.services.dynamodbv2.model.TableDescription; @@ -63,12 +64,18 @@ import org.apache.hadoop.io.retry.RetryPolicy; import static java.lang.String.valueOf; + +import static org.apache.commons.lang3.StringUtils.isEmpty; + import static org.apache.hadoop.fs.s3a.Constants.S3GUARD_DDB_TABLE_CAPACITY_READ_DEFAULT; import static org.apache.hadoop.fs.s3a.Constants.S3GUARD_DDB_TABLE_CAPACITY_READ_KEY; import static org.apache.hadoop.fs.s3a.Constants.S3GUARD_DDB_TABLE_CAPACITY_WRITE_DEFAULT; import static org.apache.hadoop.fs.s3a.Constants.S3GUARD_DDB_TABLE_CAPACITY_WRITE_KEY; import static org.apache.hadoop.fs.s3a.Constants.S3GUARD_DDB_TABLE_CREATE_KEY; +import static org.apache.hadoop.fs.s3a.Constants.S3GUARD_DDB_TABLE_SSE_CMK; +import static org.apache.hadoop.fs.s3a.Constants.S3GUARD_DDB_TABLE_SSE_ENABLED; import static org.apache.hadoop.fs.s3a.Constants.S3GUARD_DDB_TABLE_TAG; +import static org.apache.hadoop.fs.s3a.S3AUtils.lookupPassword; import static org.apache.hadoop.fs.s3a.S3AUtils.translateDynamoDBException; import static org.apache.hadoop.fs.s3a.S3AUtils.translateException; import static org.apache.hadoop.fs.s3a.s3guard.DynamoDBMetadataStore.E_ON_DEMAND_NO_SET_CAPACITY; @@ -102,6 +109,9 @@ public class DynamoDBMetadataStoreTableManager { public static final String E_INCOMPATIBLE_ITEM_VERSION = "Database table is from an incompatible S3Guard version based on table ITEM."; + /** The AWS managed CMK for DynamoDB server side encryption. */ + public static final String SSE_DEFAULT_MASTER_KEY = "alias/aws/dynamodb"; + /** Invoker for IO. Until configured properly, use try-once. */ private Invoker invoker = new Invoker(RetryPolicies.TRY_ONCE_THEN_FAIL, Invoker.NO_OP @@ -298,6 +308,7 @@ private void createTable(ProvisionedThroughput capacity) throws IOException { .withTableName(tableName) .withKeySchema(keySchema()) .withAttributeDefinitions(attributeDefinitions()) + .withSSESpecification(getSseSpecFromConfig()) .withTags(getTableTagsFromConfig()); if (capacity != null) { mode = String.format("with provisioned read capacity %d and" @@ -322,6 +333,39 @@ private void createTable(ProvisionedThroughput capacity) throws IOException { putVersionMarkerItemToTable(); } + /** + * Get DynamoDB table server side encryption (SSE) settings from configuration. + */ + private SSESpecification getSseSpecFromConfig() { + final SSESpecification sseSpecification = new SSESpecification(); + boolean enabled = conf.getBoolean(S3GUARD_DDB_TABLE_SSE_ENABLED, false); + if (!enabled) { + // Do not set other options if SSE is disabled. Otherwise it will throw + // ValidationException. + return sseSpecification; + } + sseSpecification.setEnabled(Boolean.TRUE); + String cmk = null; + try { + // Get DynamoDB table SSE CMK from a configuration/credential provider. + cmk = lookupPassword("", conf, S3GUARD_DDB_TABLE_SSE_CMK); + } catch (IOException e) { + LOG.error("Cannot retrieve " + S3GUARD_DDB_TABLE_SSE_CMK, e); + } + if (isEmpty(cmk)) { + // Using Amazon managed default master key for DynamoDB table + return sseSpecification; + } + if (SSE_DEFAULT_MASTER_KEY.equals(cmk)) { + LOG.warn("Ignoring default DynamoDB table KMS Master Key {}", + SSE_DEFAULT_MASTER_KEY); + } else { + sseSpecification.setSSEType("KMS"); + sseSpecification.setKMSMasterKeyId(cmk); + } + return sseSpecification; + } + /** * Return tags from configuration and the version marker for adding to * dynamo table during creation. diff --git a/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/s3guard/S3GuardTool.java b/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/s3guard/S3GuardTool.java index 03ca33a465..cfd67b4b3a 100644 --- a/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/s3guard/S3GuardTool.java +++ b/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/s3guard/S3GuardTool.java @@ -73,6 +73,7 @@ import static org.apache.hadoop.fs.s3a.S3AUtils.propagateBucketOptions; import static org.apache.hadoop.fs.s3a.commit.CommitConstants.*; import static org.apache.hadoop.fs.s3a.commit.staging.StagingCommitterConstants.FILESYSTEM_TEMP_PATH; +import static org.apache.hadoop.fs.s3a.s3guard.DynamoDBMetadataStoreTableManager.SSE_DEFAULT_MASTER_KEY; import static org.apache.hadoop.service.launcher.LauncherExitCodes.*; /** @@ -143,6 +144,8 @@ public abstract class S3GuardTool extends Configured implements Tool, public static final String REGION_FLAG = "region"; public static final String READ_FLAG = "read"; public static final String WRITE_FLAG = "write"; + public static final String SSE_FLAG = "sse"; + public static final String CMK_FLAG = "cmk"; public static final String TAG_FLAG = "tag"; public static final String VERBOSE = "verbose"; @@ -509,6 +512,8 @@ static class Init extends S3GuardTool { " -" + REGION_FLAG + " REGION - Service region for connections\n" + " -" + READ_FLAG + " UNIT - Provisioned read throughput units\n" + " -" + WRITE_FLAG + " UNIT - Provisioned write through put units\n" + + " -" + SSE_FLAG + " - Enable server side encryption\n" + + " -" + CMK_FLAG + " KEY - Customer managed CMK\n" + " -" + TAG_FLAG + " key=value; list of tags to tag dynamo table\n" + "\n" + " URLs for Amazon DynamoDB are of the form dynamodb://TABLE_NAME.\n" + @@ -518,11 +523,13 @@ static class Init extends S3GuardTool { + "capacities to 0"; Init(Configuration conf) { - super(conf); + super(conf, SSE_FLAG); // read capacity. getCommandFormat().addOptionWithValue(READ_FLAG); // write capacity. getCommandFormat().addOptionWithValue(WRITE_FLAG); + // customer managed customer master key (CMK) for server side encryption + getCommandFormat().addOptionWithValue(CMK_FLAG); // tag getCommandFormat().addOptionWithValue(TAG_FLAG); } @@ -546,13 +553,13 @@ public int run(String[] args, PrintStream out) throws Exception { errorln(USAGE); throw e; } - - String readCap = getCommandFormat().getOptValue(READ_FLAG); + CommandFormat commands = getCommandFormat(); + String readCap = commands.getOptValue(READ_FLAG); if (readCap != null && !readCap.isEmpty()) { int readCapacity = Integer.parseInt(readCap); getConf().setInt(S3GUARD_DDB_TABLE_CAPACITY_READ_KEY, readCapacity); } - String writeCap = getCommandFormat().getOptValue(WRITE_FLAG); + String writeCap = commands.getOptValue(WRITE_FLAG); if (writeCap != null && !writeCap.isEmpty()) { int writeCapacity = Integer.parseInt(writeCap); getConf().setInt(S3GUARD_DDB_TABLE_CAPACITY_WRITE_KEY, writeCapacity); @@ -565,7 +572,25 @@ public int run(String[] args, PrintStream out) throws Exception { setConf(bucketConf); } - String tags = getCommandFormat().getOptValue(TAG_FLAG); + String cmk = commands.getOptValue(CMK_FLAG); + if (commands.getOpt(SSE_FLAG)) { + getConf().setBoolean(S3GUARD_DDB_TABLE_SSE_ENABLED, true); + LOG.debug("SSE flag is passed to command {}", this.getName()); + if (!StringUtils.isEmpty(cmk)) { + if (SSE_DEFAULT_MASTER_KEY.equals(cmk)) { + LOG.warn("Ignoring default DynamoDB table KMS Master Key " + + "alias/aws/dynamodb in configuration"); + } else { + LOG.debug("Setting customer managed CMK {}", cmk); + getConf().set(S3GUARD_DDB_TABLE_SSE_CMK, cmk); + } + } + } else if (!StringUtils.isEmpty(cmk)) { + throw invalidArgs("Option %s can only be used with option %s", + CMK_FLAG, SSE_FLAG); + } + + String tags = commands.getOptValue(TAG_FLAG); if (tags != null && !tags.isEmpty()) { String[] stringList = tags.split(";"); Map tagsKV = new HashMap<>(); diff --git a/hadoop-tools/hadoop-aws/src/site/markdown/tools/hadoop-aws/s3guard.md b/hadoop-tools/hadoop-aws/src/site/markdown/tools/hadoop-aws/s3guard.md index bca3744a79..5219afd122 100644 --- a/hadoop-tools/hadoop-aws/src/site/markdown/tools/hadoop-aws/s3guard.md +++ b/hadoop-tools/hadoop-aws/src/site/markdown/tools/hadoop-aws/s3guard.md @@ -423,6 +423,39 @@ This is the default, as configured in the default configuration options. ``` +### 8. If creating a table: Enable server side encryption (SSE) + +Encryption at rest can help you protect sensitive data in your DynamoDB table. +When creating a new table, you can set server side encryption on the table +using the default AWS owned customer master key (CMK), AWS managed CMK, or +customer managed CMK. S3Guard code accessing the table is all the same whether +SSE is enabled or not. For more details on DynamoDB table server side +encryption, see the AWS page on [Encryption at Rest: How It Works](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/encryption.howitworks.html). + +These are the default configuration options, as configured in `core-default.xml`. + +```xml + + fs.s3a.s3guard.ddb.table.sse.enabled + false + + Whether server-side encryption (SSE) is enabled or disabled on the table. + By default it's disabled, meaning SSE is set to AWS owned CMK. + + + + + fs.s3a.s3guard.ddb.table.sse.cmk + + + The KMS Customer Master Key (CMK) used for the KMS encryption on the table. + To specify a CMK, this config value can be its key ID, Amazon Resource Name + (ARN), alias name, or alias ARN. Users only need to provide this config if + the key is different from the default DynamoDB KMS Master Key, which is + alias/aws/dynamodb. + + +``` ## Authenticating with S3Guard @@ -583,6 +616,16 @@ of the table. [-write PROVISIONED_WRITES] [-read PROVISIONED_READS] ``` +Server side encryption (SSE) can be enabled with AWS managed customer master key +(CMK), or customer managed CMK. By default the DynamoDB table will be encrypted +with AWS owned CMK. To use a customer managed CMK, you can specify its KMS key +ID, ARN, alias name, or alias ARN. If not specified, the default AWS managed CMK +for DynamoDB "alias/aws/dynamodb" will be used. + +```bash +[-sse [-cmk KMS_CMK_ID]] +``` + Tag argument can be added with a key=value list of tags. The table for the metadata store will be created with these tags in DynamoDB. @@ -590,6 +633,7 @@ metadata store will be created with these tags in DynamoDB. [-tag key=value;] ``` + Example 1 ```bash @@ -608,6 +652,7 @@ hadoop s3guard init -meta dynamodb://ireland-team -region eu-west-1 --read 0 --w Creates a table "ireland-team" in the region "eu-west-1.amazonaws.com" + Example 3 ```bash @@ -619,6 +664,17 @@ write capacity will be those of the site configuration's values of `fs.s3a.s3guard.ddb.table.capacity.read` and `fs.s3a.s3guard.ddb.table.capacity.write`; if these are both zero then it will be an on-demand table. + +Example 4 + +```bash +hadoop s3guard init -meta dynamodb://ireland-team -sse +``` + +Creates a table "ireland-team" with server side encryption enabled. The CMK will +be using the default AWS managed "alias/aws/dynamodb". + + ### Import a bucket: `s3guard import` ```bash diff --git a/hadoop-tools/hadoop-aws/src/site/markdown/tools/hadoop-aws/testing.md b/hadoop-tools/hadoop-aws/src/site/markdown/tools/hadoop-aws/testing.md index 6eea3bfba1..ca35ce8f66 100644 --- a/hadoop-tools/hadoop-aws/src/site/markdown/tools/hadoop-aws/testing.md +++ b/hadoop-tools/hadoop-aws/src/site/markdown/tools/hadoop-aws/testing.md @@ -1259,6 +1259,27 @@ during the use of a S3Guarded S3A filesystem are wrapped by retry logic. *The best way to verify resilience is to run the entire `hadoop-aws` test suite, or even a real application, with throttling enabled. +### Testing encrypted DynamoDB tables + +By default, a DynamoDB table is encrypted using AWS owned customer master key +(CMK). You can enable server side encryption (SSE) using AWS managed CMK or +customer managed CMK in KMS before running the S3Guard tests. +1. To enable AWS managed CMK, set the config +`fs.s3a.s3guard.ddb.table.sse.enabled` to true in `auth-keys.xml`. +1. To enable customer managed CMK, you need to create a KMS key and set the +config in `auth-keys.xml`. The value can be the key ARN or alias. Example: +``` + + fs.s3a.s3guard.ddb.table.sse.enabled + true + + + fs.s3a.s3guard.ddb.table.sse.cmk + arn:aws:kms:us-west-2:360379543683:key/071a86ff-8881-4ba0-9230-95af6d01ca01 + +``` +For more details about SSE on DynamoDB table, please see [S3Guard doc](./s3guard.html). + ### Testing only: Local Metadata Store There is an in-memory Metadata Store for testing. diff --git a/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/s3guard/ITestDynamoDBMetadataStore.java b/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/s3guard/ITestDynamoDBMetadataStore.java index 89c401b787..2bcc98bc8d 100644 --- a/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/s3guard/ITestDynamoDBMetadataStore.java +++ b/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/s3guard/ITestDynamoDBMetadataStore.java @@ -40,6 +40,7 @@ import com.amazonaws.services.dynamodbv2.document.Table; import com.amazonaws.services.dynamodbv2.model.ListTagsOfResourceRequest; import com.amazonaws.services.dynamodbv2.model.ResourceNotFoundException; +import com.amazonaws.services.dynamodbv2.model.SSEDescription; import com.amazonaws.services.dynamodbv2.model.TableDescription; import com.amazonaws.services.dynamodbv2.model.Tag; import com.amazonaws.services.dynamodbv2.model.TagResourceRequest; @@ -427,9 +428,11 @@ public void testInitialize() throws IOException { DynamoDBMetadataStore ddbms = new DynamoDBMetadataStore(); try { ddbms.initialize(s3afs, new S3Guard.TtlTimeProvider(conf)); - verifyTableInitialized(tableName, ddbms.getDynamoDB()); + Table table = verifyTableInitialized(tableName, ddbms.getDynamoDB()); + verifyTableSse(conf, table.getDescription()); assertNotNull(ddbms.getTable()); assertEquals(tableName, ddbms.getTable().getTableName()); + String expectedRegion = conf.get(S3GUARD_DDB_REGION_KEY, s3afs.getBucketLocation(bucket)); assertEquals("DynamoDB table should be in configured region or the same" + @@ -459,6 +462,7 @@ public void testInitializeWithConfiguration() throws IOException { fail("Should have failed because the table name is not set!"); } catch (IllegalArgumentException ignored) { } + // config table name conf.set(S3GUARD_DDB_TABLE_NAME_KEY, tableName); try (DynamoDBMetadataStore ddbms = new DynamoDBMetadataStore()) { @@ -466,12 +470,26 @@ public void testInitializeWithConfiguration() throws IOException { fail("Should have failed because as the region is not set!"); } catch (IllegalArgumentException ignored) { } + // config region conf.set(S3GUARD_DDB_REGION_KEY, savedRegion); + doTestInitializeWithConfiguration(conf, tableName); + + // config table server side encryption (SSE) + conf.setBoolean(S3GUARD_DDB_TABLE_SSE_ENABLED, true); + doTestInitializeWithConfiguration(conf, tableName); + } + + /** + * Test initialize() using a Configuration object successfully. + */ + private void doTestInitializeWithConfiguration(Configuration conf, + String tableName) throws IOException { DynamoDBMetadataStore ddbms = new DynamoDBMetadataStore(); try { ddbms.initialize(conf, new S3Guard.TtlTimeProvider(conf)); - verifyTableInitialized(tableName, ddbms.getDynamoDB()); + Table table = verifyTableInitialized(tableName, ddbms.getDynamoDB()); + verifyTableSse(conf, table.getDescription()); assertNotNull(ddbms.getTable()); assertEquals(tableName, ddbms.getTable().getTableName()); assertEquals("Unexpected key schema found!", @@ -1108,6 +1126,25 @@ private Table verifyTableInitialized(String tableName, DynamoDB dynamoDB) { return table; } + /** + * Verify the table is created with correct server side encryption (SSE). + */ + private void verifyTableSse(Configuration conf, TableDescription td) { + SSEDescription sseDescription = td.getSSEDescription(); + if (conf.getBoolean(S3GUARD_DDB_TABLE_SSE_ENABLED, false)) { + assertNotNull(sseDescription); + assertEquals("ENABLED", sseDescription.getStatus()); + assertEquals("KMS", sseDescription.getSSEType()); + // We do not test key ARN is the same as configured value, + // because in configuration, the ARN can be specified by alias. + assertNotNull(sseDescription.getKMSMasterKeyArn()); + } else { + if (sseDescription != null) { + assertEquals("DISABLED", sseDescription.getStatus()); + } + } + } + /** * This validates the table is not found in DynamoDB. * diff --git a/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/s3guard/ITestS3GuardToolDynamoDB.java b/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/s3guard/ITestS3GuardToolDynamoDB.java index cd8d1d202a..915f1cc190 100644 --- a/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/s3guard/ITestS3GuardToolDynamoDB.java +++ b/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/s3guard/ITestS3GuardToolDynamoDB.java @@ -199,6 +199,7 @@ public void testDynamoDBInitDestroyCycle() throws Throwable { Init.NAME, "-" + READ_FLAG, "0", "-" + WRITE_FLAG, "0", + "-" + Init.SSE_FLAG, "-" + META_FLAG, "dynamodb://" + testTableName, testS3Url); } @@ -232,8 +233,6 @@ public void testDynamoDBInitDestroyCycle() throws Throwable { testS3Url); assertTrue("No Dynamo diagnostics in output " + info, info.contains(DESCRIPTION)); - assertTrue("No Dynamo diagnostics in output " + info, - info.contains(DESCRIPTION)); } // get the current values to set again @@ -353,4 +352,17 @@ public void testCLIFsckCheckExclusive() throws Exception { "-" + Fsck.DDB_MS_CONSISTENCY_FLAG, "-" + Fsck.CHECK_FLAG, "s3a://" + getFileSystem().getBucket())); } + + /** + * Test that when init, the CMK option can not live without SSE enabled. + */ + @Test + public void testCLIInitParamCmkWithoutSse() throws Exception { + intercept(ExitUtil.ExitException.class, "can only be used with", + () -> run(S3GuardTool.Init.NAME, + "-" + S3GuardTool.CMK_FLAG, + "alias/" + UUID.randomUUID(), + "s3a://" + getFileSystem().getBucket() + "/" + UUID.randomUUID())); + } + }