Skip to content

Commit 9094f79

Browse files
authored
Ensure necessary security context for s3 bulk deletions (#108280) (#108299)
This PR moves the doPrivileged wrapper closer to the actual deletion request to ensure the necesary security context is established at all times. Also added a new repository setting to configure max size for s3 deleteObjects request. Fixes: #108049 (cherry picked from commit bcf4297) # Conflicts: # docs/reference/snapshot-restore/repository-s3.asciidoc # modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3BlobStore.java
1 parent a337e2e commit 9094f79

File tree

5 files changed

+42
-12
lines changed

5 files changed

+42
-12
lines changed

docs/changelog/108280.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pr: 108280
2+
summary: Ensure necessary security context for s3 bulk deletions
3+
area: Snapshot/Restore
4+
type: bug
5+
issues:
6+
- 108049

docs/reference/snapshot-restore/repository-s3.asciidoc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,14 @@ include::repository-shared-settings.asciidoc[]
301301
`intelligent_tiering`. Defaults to `standard`. See
302302
<<repository-s3-storage-classes>> for more information.
303303

304+
`delete_objects_max_size`::
305+
306+
(<<number,numeric>>) Sets the maxmimum batch size, betewen 1 and 1000, used
307+
for `DeleteObjects` requests. Defaults to 1000 which is the maximum number
308+
supported by the
309+
https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjects.html[AWS
310+
DeleteObjects API].
311+
304312
NOTE: The option of defining client settings in the repository settings as
305313
documented below is considered deprecated, and will be removed in a future
306314
version.

modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3BlobStore.java

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ class S3BlobStore implements BlobStore {
6262
* Maximum number of deletes in a {@link DeleteObjectsRequest}.
6363
* @see <a href="https://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html">S3 Documentation</a>.
6464
*/
65-
private static final int MAX_BULK_DELETES = 1000;
65+
static final int MAX_BULK_DELETES = 1000;
6666

6767
private static final Logger logger = LogManager.getLogger(S3BlobStore.class);
6868

@@ -88,6 +88,8 @@ class S3BlobStore implements BlobStore {
8888

8989
private final StatsCollectors statsCollectors = new StatsCollectors();
9090

91+
private final int bulkDeletionBatchSize;
92+
9193
S3BlobStore(
9294
S3Service service,
9395
String bucket,
@@ -111,6 +113,7 @@ class S3BlobStore implements BlobStore {
111113
this.threadPool = threadPool;
112114
this.snapshotExecutor = threadPool.executor(ThreadPool.Names.SNAPSHOT);
113115
this.repositoriesMetrics = repositoriesMetrics;
116+
this.bulkDeletionBatchSize = S3Repository.DELETION_BATCH_SIZE_SETTING.get(repositoryMetadata.settings());
114117
}
115118

116119
RequestMetricCollector getMetricCollector(Operation operation, OperationPurpose purpose) {
@@ -308,18 +311,16 @@ public void deleteBlobsIgnoringIfNotExists(OperationPurpose purpose, Iterator<St
308311
try (AmazonS3Reference clientReference = clientReference()) {
309312
// S3 API only allows 1k blobs per delete so we split up the given blobs into requests of max. 1k deletes
310313
final AtomicReference<Exception> aex = new AtomicReference<>();
311-
SocketAccess.doPrivilegedVoid(() -> {
312-
blobNames.forEachRemaining(key -> {
313-
partition.add(key);
314-
if (partition.size() == MAX_BULK_DELETES) {
315-
deletePartition(purpose, clientReference, partition, aex);
316-
partition.clear();
317-
}
318-
});
319-
if (partition.isEmpty() == false) {
314+
blobNames.forEachRemaining(key -> {
315+
partition.add(key);
316+
if (partition.size() == bulkDeletionBatchSize) {
320317
deletePartition(purpose, clientReference, partition, aex);
318+
partition.clear();
321319
}
322320
});
321+
if (partition.isEmpty() == false) {
322+
deletePartition(purpose, clientReference, partition, aex);
323+
}
323324
if (aex.get() != null) {
324325
throw aex.get();
325326
}
@@ -335,7 +336,7 @@ private void deletePartition(
335336
AtomicReference<Exception> aex
336337
) {
337338
try {
338-
clientReference.client().deleteObjects(bulkDelete(purpose, this, partition));
339+
SocketAccess.doPrivilegedVoid(() -> clientReference.client().deleteObjects(bulkDelete(purpose, this, partition)));
339340
} catch (MultiObjectDeleteException e) {
340341
// We are sending quiet mode requests so we can't use the deleted keys entry on the exception and instead
341342
// first remove all keys that were sent in the request and then add back those that ran into an exception.

modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Repository.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,16 @@ class S3Repository extends MeteredBlobStoreRepository {
173173
*/
174174
static final Setting<String> BASE_PATH_SETTING = Setting.simpleString("base_path");
175175

176+
/**
177+
* The batch size for DeleteObjects request
178+
*/
179+
static final Setting<Integer> DELETION_BATCH_SIZE_SETTING = Setting.intSetting(
180+
"delete_objects_max_size",
181+
S3BlobStore.MAX_BULK_DELETES,
182+
1,
183+
S3BlobStore.MAX_BULK_DELETES
184+
);
185+
176186
private final S3Service service;
177187

178188
private final String bucket;

x-pack/plugin/snapshot-repo-test-kit/qa/s3/src/javaRestTest/java/org/elasticsearch/repositories/blobstore/testkit/S3SnapshotRepoTestKitIT.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ protected Settings repositorySettings() {
5959
final String basePath = System.getProperty("test.s3.base_path");
6060
assertThat(basePath, not(blankOrNullString()));
6161

62-
return Settings.builder().put("client", "repo_test_kit").put("bucket", bucket).put("base_path", basePath).build();
62+
return Settings.builder()
63+
.put("client", "repo_test_kit")
64+
.put("bucket", bucket)
65+
.put("base_path", basePath)
66+
.put("delete_objects_max_size", between(1, 1000))
67+
.build();
6368
}
6469
}

0 commit comments

Comments
 (0)