Repository: floci-io/floci (2026-07-11T10:02:16Z)

Issue:
[BUG] S3 GetBucketEncryption returns 404 instead of default SSE-S3
## Service

S3

## AWS API Action

GetBucketEncryption

## Expected behavior

Since 2023-01-05 every S3 bucket has SSE-S3 (`AES256`) default encryption. `GetBucketEncryption` on a bucket with no explicit config returns that default config with `200`:

```json
{"ServerSideEncryptionConfiguration":{"Rules":[{"ApplyServerSideEncryptionByDefault":{"SSEAlgorithm":"AES256"},"BucketKeyEnabled":false}]}}
```

## Actual behavior

Floci returns pre-2023 behavior — a `404`:

```
An error occurred (ServerSideEncryptionConfigurationNotFoundError) when calling the GetBucketEncryption operation: The server side encryption configuration was not found
```

This breaks clients that assume the modern default is always present (e.g. the AWS Controllers for Kubernetes s3-controller).

## Reproduction

```bash
aws --endpoint-url http://localhost:4566 s3api create-bucket --bucket repro-enc
aws --endpoint-url http://localhost:4566 s3api get-bucket-encryption --bucket repro-enc
# => An error occurred (ServerSideEncryptionConfigurationNotFoundError) ...
```

## Environment

- Floci version / image tag: 1.5.32
- Java SDK version (if applicable): n/a — AWS CLI 2.35.7
- How you're running Floci: Docker



Discussion:

Patch:
diff --git a/src/main/java/io/github/hectorvent/floci/services/s3/S3Service.java b/src/main/java/io/github/hectorvent/floci/services/s3/S3Service.java
index c60cc0f03d..571aaac738 100644
--- a/src/main/java/io/github/hectorvent/floci/services/s3/S3Service.java
+++ b/src/main/java/io/github/hectorvent/floci/services/s3/S3Service.java
@@ -1757,12 +1757,28 @@ public void putObjectAcl(String bucketName, String key, String versionId, String
         objectStore.put(storeKey, obj);
     }
 
+    /**
+     * Returns the bucket's server-side encryption configuration as XML.
+     * <p>
+     * Buckets that have
+     * never been configured return the AWS default (SSE-S3 / {@code AES256});
+     * since January 2023 AWS applies SSE-S3 as the base level of encryption on
+     * every bucket and never returns 404 for {@code GetBucketEncryption}.
+     */
     public String getBucketEncryption(String bucketName) {
         Bucket bucket = bucketStore.get(bucketName)
                 .orElseThrow(() -> new AwsException("NoSuchBucket", "The specified bucket does not exist.", 404));
         if (bucket.getEncryptionConfiguration() == null) {
-            throw new AwsException("ServerSideEncryptionConfigurationNotFoundError",
-                    "The server side encryption configuration was not found", 404);
+            return new XmlBuilder()
+                    .start("ServerSideEncryptionConfiguration", AwsNamespaces.S3)
+                      .start("Rule")
+                        .start("ApplyServerSideEncryptionByDefault")
+                          .elem("SSEAlgorithm", "AES256")
+                        .end("ApplyServerSideEncryptionByDefault")
+                        .elem("BucketKeyEnabled", "false")
+                      .end("Rule")
+                    .end("ServerSideEncryptionConfiguration")
+                    .build();
         }
         return bucket.getEncryptionConfiguration();
     }


Test patch:
diff --git a/src/test/java/io/github/hectorvent/floci/services/s3/S3EncryptionIntegrationTest.java b/src/test/java/io/github/hectorvent/floci/services/s3/S3EncryptionIntegrationTest.java
new file mode 100644
index 0000000000..230826e752
--- /dev/null
+++ b/src/test/java/io/github/hectorvent/floci/services/s3/S3EncryptionIntegrationTest.java
@@ -0,0 +1,113 @@
+package io.github.hectorvent.floci.services.s3;
+
+import io.quarkus.test.junit.QuarkusTest;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+
+import static io.restassured.RestAssured.given;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.not;
+
+@QuarkusTest
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+class S3EncryptionIntegrationTest {
+
+    private static final String BUCKET = "encryption-int-test";
+    private static final String SSE_KMS_XML = """
+            <ServerSideEncryptionConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
+                <Rule>
+                    <ApplyServerSideEncryptionByDefault>
+                        <SSEAlgorithm>aws:kms</SSEAlgorithm>
+                        <KMSMasterKeyID>arn:aws:kms:us-east-1:000000000000:key/abc</KMSMasterKeyID>
+                    </ApplyServerSideEncryptionByDefault>
+                    <BucketKeyEnabled>true</BucketKeyEnabled>
+                </Rule>
+            </ServerSideEncryptionConfiguration>
+            """;
+
+    @Test
+    @Order(1)
+    void createBucket() {
+        given()
+        .when()
+            .put("/" + BUCKET)
+        .then()
+            .statusCode(200);
+    }
+
+    /**
+     * Since January 2023 AWS applies SSE-S3 (AES256) as the base level of encryption on every
+     * bucket, and {@code GetBucketEncryption} returns that default configuration rather than
+     * {@code 404 ServerSideEncryptionConfigurationNotFoundError}. The ACK s3-controller reads
+     * {@code getEncryptionResponse.ServerSideEncryptionConfiguration.Rules} without a nil guard,
+     * so a 404 here crashes it; returning the default keeps Floci AWS-faithful and unblocks ACK.
+     */
+    @Test
+    @Order(2)
+    void getEncryptionBeforePutReturnsDefaultSseS3() {
+        given()
+        .when()
+            .get("/" + BUCKET + "?encryption")
+        .then()
+            .statusCode(200)
+            .body(containsString("<ServerSideEncryptionConfiguration"))
+            .body(containsString("<SSEAlgorithm>AES256</SSEAlgorithm>"))
+            .body(not(containsString("ServerSideEncryptionConfigurationNotFoundError")));
+    }
+
+    @Test
+    @Order(3)
+    void putEncryptionReturns200() {
+        given()
+            .body(SSE_KMS_XML)
+        .when()
+            .put("/" + BUCKET + "?encryption")
+        .then()
+            .statusCode(200);
+    }
+
+    @Test
+    @Order(4)
+    void getEncryptionReturnsStoredConfiguration() {
+        given()
+        .when()
+            .get("/" + BUCKET + "?encryption")
+        .then()
+            .statusCode(200)
+            .body(containsString("<SSEAlgorithm>aws:kms</SSEAlgorithm>"));
+    }
+
+    @Test
+    @Order(5)
+    void getEncryptionOnMissingBucketReturns404() {
+        given()
+        .when()
+            .get("/this-bucket-does-not-exist-enc?encryption")
+        .then()
+            .statusCode(404)
+            .body(containsString("NoSuchBucket"));
+    }
+
+    /**
+     * After DeleteBucketEncryption clears the explicit config, GetBucketEncryption must fall back
+     * to the AWS default SSE-S3 (AES256) — not the previously stored KMS config and not a 404.
+     */
+    @Test
+    @Order(6)
+    void getEncryptionAfterDeleteReturnsDefaultSseS3() {
+        given()
+        .when()
+            .delete("/" + BUCKET + "?encryption")
+        .then()
+            .statusCode(204);
+        given()
+        .when()
+            .get("/" + BUCKET + "?encryption")
+        .then()
+            .statusCode(200)
+            .body(containsString("<SSEAlgorithm>AES256</SSEAlgorithm>"))
+            .body(not(containsString("aws:kms")));
+    }
+}