I am getting Access Denied 403 when trying to access a signed Cloudfront URL to an S3 bucket. The bucket is private and has blocked all public access, and I also have an OAC defined that grants permissions to CF to the S3 bucket.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "cloudfront.amazonaws.com"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::<bucket_name>/*",
"Condition": {
"StringEquals": {
"aws:SourceArn": "arn:aws:cloudfront::<id>:distribution/<id>"
}
}
}
] }
I'm signing these URLs using the Python SDK
def get_rsa_signer(cls, message):
kms_client = boto3.client("kms")
response = kms_client.sign(
KeyId=secrets_manager.get_env("CLOUDFRONT_KMS_PRIVATE_KEY_ID"),
Message=message,
MessageType="RAW",
SigningAlgorithm="RSASSA_PKCS1_V1_5_SHA_256",
)
return response["Signature"]
cloudfront_signer = CloudFrontSigner( secrets_manager.get_env("CLOUDFRONT_PUBLIC_KEY_ID"),MediaStorage.get_rsa_signer)
url = cloudfront_signer.generate_presigned_url(f"{settings.AWS_CLOUDFRONT_DOMAIN}/{file_key}",
date_less_than=timezone.now() + timedelta(days=7),
)
These are the settings on the OAC
resource "aws_cloudfront_origin_access_control" "cloudfront_backend_bucket" {
name = "${var.environment}-cloudfront_backend_bucket"
description = "OAC for S3 bucket access"
signing_behavior = "always"
signing_protocol = "sigv4"
origin_access_control_origin_type = "s3"
}
The public key ID on the URL is correct but I have no way of verifying whether the signature is correct, though I think the error in this case should be different.
It seems that the only problem is that I wasn't URL encoding the url that was to be signed. The solution was:
url = cloudfront_signer.generate_presigned_url(f"{settings.AWS_CLOUDFRONT_DOMAIN}/{urllib.parse.quote(file_key)}",
date_less_than=timezone.now() + timedelta(days=7),
)