How to migrate to CloudFront Origin Access Control from Origin Access Identity
What are the differences between OAI and the new OAC
Protecting S3 buckets
AWS recently announced the new Origin Access Control (OAC) feature for CloudFront. This is a successor of the Origin Acccess Identity (OAI) and I was naturally interested in what doors it opens in terms of new features.
OAI and OAC both have the same purpose: make CloudFront able to fetch objects from an S3 bucket that is not open to the public. This solves a narrow case for a problem with CDNs: they fetch content just like any other visitor, which makes it possible to circumvent the CDN entirely and go to the origin. Without using OAI or OAC, you'd need to make the S3 bucket public to allow CloudFront to fetch the files. But with them, the bucket can be private.
In this article, we'll discuss how to configure them and how to move from OAI to OAC.
We'll look into the 3 parts of the configuration: the entities on CloudFront's side, the Distribution config, then the bucket config on S3.
Entities
To use either of them, you need to create an entity on CloudFront. Both are available under the Security/Origin access menu:
Here, an OAI requires only a name:
OAC has a few extra options:
But apart from a longer form, I couldn't yet find a use-case where I needed anything else than the default. So we can say there are no real differences here.
In Terraform, OAC is a bit longer, but with the same information:
resource "aws_cloudfront_origin_access_control" "oac" {
name = "some-name"
description = ""
origin_access_control_origin_type = "s3"
signing_behavior = "always"
signing_protocol = "sigv4"
}
resource "aws_cloudfront_origin_access_identity" "oai" {
}
Distribution config
Both entities are configured for an origin:
And changing which one to use is to select the other one:
In Terraform:
origin {
domain_name = aws_s3_bucket.files.bucket_regional_domain_name
origin_id = "files_oac"
origin_access_control_id = aws_cloudfront_origin_access_control.oac.id
}
origin {
domain_name = aws_s3_bucket.files.bucket_regional_domain_name
origin_id = "files_oai"
s3_origin_config {
origin_access_identity = aws_cloudfront_origin_access_identity.oai.cloudfront_access_identity_path
}
}
Bucket config
Finally, the bucket needs to allow the s3:GetObject
action on its objects.
For the OAI, it is done via a special Principal
:
While for the OAC, the Principal
is the cloudfront.amazonaws.com
service and the AWS:SourceArn
defines the distribution:
In Terraform:
data "aws_iam_policy_document" "default" {
statement {
actions = ["s3:GetObject"]
resources = ["${aws_s3_bucket.files.arn}/*"]
principals {
type = "Service"
identifiers = ["cloudfront.amazonaws.com"]
}
condition {
test = "StringEquals"
variable = "AWS:SourceArn"
values = [aws_cloudfront_distribution.distribution.arn]
}
}
statement {
actions = ["s3:GetObject"]
resources = ["${aws_s3_bucket.files.arn}/*"]
principals {
type = "AWS"
identifiers = [aws_cloudfront_origin_access_identity.oai.iam_arn]
}
}
}
Conclusion
Did you spot the big difference between the two? Me neither. Their configuration is exactly the same, they allow the same use-case, and nothing else. I was hoping that maybe OAC can sign requests for Lambda origins too, making it possible to configure private endpoints without adding a secret header, but no: OAC only supports S3 origins.
The documentation lists a few examples that is now possible with OAC that weren't possible with OAI:
- All Amazon S3 buckets in all AWS Regions, including opt-in Regions launched after December 2022
- Amazon S3 server-side encryption with AWS KMS (SSE-KMS)
- Dynamic requests (POST, PUT, etc.) to Amazon S3
This sounds great, but there weren't any roadblocks why CloudFront couldn't support these use-cases from day 1. Overall, this change seems more like to reduce some internal technical debt rather than anything noteworthy. I'm still missing the ability to secure other kinds of origins.
One good thing about this change is that the bucket policy references the distribution itself and not the entity that can be attached to multiple distributions. This makes permissions a more direct connection between the bucket and the recipient.