기존 Discourse 서버를 AWS 기반 환경으로 마이그레이션하고 있으며, 업로드를 S3 버킷에 저장하는 과정에서 고객 관리 키를 사용한 서버 측 암호화(SSE-C)를 적용하고 있습니다. 복원 과정 중 업로드가 S3에 저장되지 않고 있으며, 모든 업로드가 실패하고 있습니다. tap|p 디버깅을 적절히 활용해 조사한 결과, 업로드 요청은 실제로 수행되고 있지만, 업로드 후 S3에서 반환된 etag가 매번 다르기 때문에 반환된 etag 검증이 실패하는 것으로 확인되었습니다. 예를 들어, 복원을 두 번 시도했을 때 the put_object call의 반환 값은 다음과 같습니다:
# Run 1
#<struct Aws::S3::Types::PutObjectOutput expiration=nil, etag="\"d49ec2006cfd6fe957af2f711edd9a4b\"", checksum_crc32=nil, checksum_crc32c=nil, checksum_sha1=nil, checksum_sha256=nil, server_side_encryption="aws:kms", version_id="xAF23wQ.zwpoxVmmGiTjxfX0svMZbHAe", sse_customer_algorithm=nil, sse_customer_key_md5=nil, ssekms_key_id="**redacted**", ssekms_encryption_context=nil, bucket_key_enabled=true, request_charged=nil>
# Run 2:
#<struct Aws::S3::Types::PutObjectOutput expiration=nil, etag="\"05edffee421c6aef950b3d4418ada293\"", checksum_crc32=nil, checksum_crc32c=nil, checksum_sha1=nil, checksum_sha256=nil, server_side_encryption="aws:kms", version_id="H2_8SVh.Yx2LKB4GIjhyPbVoj_.Vc1E2", sse_customer_algorithm=nil, sse_customer_key_md5=nil, ssekms_key_id="**redacted**", ssekms_encryption_context=nil, bucket_key_enabled=true, request_charged=nil>
(클라이언트 측 MD5 합과 파일명을 포함하는 put_object 요청 옵션도 함께 출력하고 있으므로, 이 두 파일이 동일하다는 것을 알고 있습니다.)
결국 ETag 응답 헤더는 SSE-C를 사용할 때… 다르게 동작합니다:
고객 제공 키(SSE-C) 또는 AWS Key Management Service(AWS KMS) 키(SSE-KMS)로 암호화된 객체의 ETag는 객체 데이터의 MD5 다이제스트가 아닙니다.
SSE-C 환경에서 업로드 무결성 검증을 수행할 수 있는 유일한 방법은 Content-MD5 요청 헤더를 보내고 S3가 손상 감지를 수행하도록 하는 것입니다. 또한 이미 업로드된 확인 로직도 SSE-C를 사용할 경우 깨지지만, 적어도 SKIP_ETAG_VERIFY를 사용하여 비활성화할 수는 있습니다.
즉시 PR을 제출하지 않는 이유는 이 문제를 해결하는 두 가지 다른 접근 방식이 있기 때문입니다:
SKIP_ETAG_VERIFY를 업로드 후 검증까지 확장하는 것입니다. 이는 저렴하고 해킹적인 방식이며, 사용자가 SSE-C를 사용한다는 사실을 알고 해당 옵션을 활성화해야 한다는 점을 인지해야 합니다.- 업로드 무결성 보호를 위해 Content-MD5 헤더(가능하면 항상)를 사용하는 방식으로 전환하는 것입니다. 이는 모든 사용자에게 작동한다는 장점이 있지만, 훨씬 더 큰 규모의 PR이 필요합니다.
(참고로, 이전에 아무도 이 문제에 부딪히지 않았다는 점이 다소 우려스럽습니다. 아무도 업로드에 SSE-C를 사용하여 Discourse를 사용하지 않는 걸까요?!?)