I have found aws documentation for doing this with Java, and a couple of scattered references for javascript developers, but I have not been able to accomplish this without receiving access denied from aws. I've tried a lot of different variations.
To make matters a little worse, my development environment is a proprietary framework that handles a lot of the role and credentialling in the background, but I have been able to identify that the ksm policy is the sticking point, and I have not found the solution.
I've tried passing parameters to the signing process:
const params = {
Bucket: targetBucket,
ServerSideEncryption: 'aws:kms',
SSEKMSKeyId: keyId,
Conditions: [{ acl: 'private' }, { key: filepath } ]
};
return new Promise((res, rej) => {
clientS3.createPresignedPost(params, (err, data) => {
if (err) {
console.log(err.message);
rej(err);
} else {
console.log(data);
res({ data, filepath, encryption, bucket });
}
});
});
That didn't work. Access denied. (Yes, I included these values in the formdata, to ensure a correctly signed request.)
I also tried adding headers to the post request itself, via:
return axios
.post(response.data.url, formData, {
headers: {
'Content-Type': 'multipart/form-data',
'x-amz-server-side-encryption-aws-kms-key-id': response.encryption,
'x-amz-server-side-encryption-context': bucketArn
},
....
Access Denied. I've tried mixing and matching approaches. I can make things worse by breaking the signature, but I can't land the encrypted file or find documentation to accomplish this.
UPDATE : I have verified that the access role has KMS permissions to Encrypt & GenerateDataKey, as per jarmod; AND I have verified that removing the encryption policies on the bucket allow the upload no problem.
The policy described is:
{
"Effect": "Deny",
"Principal": "*",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::[[ bucket name ]]/*",
"Condition": {
"StringNotLikeIfExists": {
"s3:x-amz-server-side-encryption-aws-kms-key-id": "[[ kms arn ]]"
}
}
}
UPDATE
After adding the header x-amz-server-side-encryption: aws:kms
I got no better result. The full request headers are:
Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Content-Length: 307546
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary4B20k5OmUGzGhYoV
Host: s3.us-west-2.amazonaws.com
Origin: http://localhost:8888
Referer: http://localhost:8888/
sec-ch-ua: "Google Chrome";v="107", "Chromium";v="107", "Not=A?Brand";v="24"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "macOS"
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36
x-amz-server-side-encryption: aws:kms
x-amz-server-side-encryption-aws-kms-key-id: [[ full arn of kms key ]]
x-amz-server-side-encryption-context: arn:aws:s3:::[[ bucket name ]]/*
The response headers are:
Request URL: https://s3.us-west-2.amazonaws.com/[[ bucket name ]]
Request Method: POST
Status Code: 403 Forbidden
Remote Address: 52.92.145.80:443
Referrer Policy: strict-origin-when-cross-origin
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Origin: http://localhost:8888
Connection: close
Content-Type: application/xml
Date: Mon, 05 Dec 2022 02:07:11 GMT
Server: AmazonS3
Transfer-Encoding: chunked
Vary: Origin, Access-Control-Request-Headers, Access-Control-Request-Method
x-amz-id-2: [[ ugly amz string ]]
x-amz-request-id: [[ shorter amz string ]]
The response payload is:
<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>AccessDenied</Code><Message>Access Denied</Message>.
<RequestId>[[ same as header request id ]]</RequestId>.
<HostId>[[ same as long amz id in headers ]] </HostId></Error>
Finally, the payload of the formdata is:
acl: private
key: [[ filename ]]
bucket: [[ bucket ]]
X-Amz-Algorithm: AWS4-HMAC-SHA256
X-Amz-Credential: [[credential string ]]
X-Amz-Date: 20221205T020711Z
X-Amz-Security-Token: [[ token ]]
Policy: [[ policy string ]]
X-Amz-Signature: [[ signature string ]]
file: (binary)
Here's a working example of a S3 Presigned URL upload with Customer-managed KMS key encryption. I used the JS SDK v3 @aws-sdk/s3-request-presigner package for signing and Node18's native fetch.
I tested it with two Bucket Policy scenarios: (a) your bucket policy verbatim (b) the policy from this AWS Knowledge Center Q&A . The upload succeeds against both policies with the headers below. As expected, it fails with a 403
response if the required headers are removed.
const body = "Uploaded with a pre-signed URL and Customer-managed KMS key!";
const keyId = "arn:aws:kms:us-east-1:123456789012:key/231389a0-...";
const command = new PutObjectCommand({
Bucket: "my-bucket-123456789012-us-east-1",
Key: `myFile.txt`,
ServerSideEncryption: "aws:kms",
SSEKMSKeyId: keyId,
Body: body,
});
const url = await getSignedUrl(client, command, { expiresIn: 3600 });
const res = await fetch(url, {
method: "PUT",
headers: {
"x-amz-server-side-encryption": "aws:kms",
"x-amz-server-side-encryption-aws-kms-key-id": keyId,
},
body,
});
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.