342 lines
17 KiB
Markdown
342 lines
17 KiB
Markdown
# CloudFront
|
|
|
|
CloudFront management-plane and local content-delivery emulation. Supports distribution lifecycle,
|
|
cache policies, origin request policies, response headers policies, origin access controls, origin
|
|
access identities, public keys, trusted key groups, CloudFront Functions, invalidations, tagging, and
|
|
GET/HEAD/OPTIONS delivery from S3 or custom origins.
|
|
|
|
**Protocol:** REST XML
|
|
**API version:** `2020-05-31`
|
|
**Endpoint prefix:** `cloudfront`
|
|
**Namespace:** `http://cloudfront.amazonaws.com/doc/2020-05-31/`
|
|
**Global service** — ARNs contain no region segment.
|
|
|
|
## Supported Operations
|
|
|
|
### Distributions
|
|
|
|
| Operation | Method | Path |
|
|
|---|---|---|
|
|
| `CreateDistribution` | POST | `/2020-05-31/distribution` |
|
|
| `CreateDistributionWithTags` | POST | `/2020-05-31/distribution?WithTags` |
|
|
| `GetDistribution` | GET | `/2020-05-31/distribution/{Id}` |
|
|
| `GetDistributionConfig` | GET | `/2020-05-31/distribution/{Id}/config` |
|
|
| `UpdateDistribution` | PUT | `/2020-05-31/distribution/{Id}/config` |
|
|
| `DeleteDistribution` | DELETE | `/2020-05-31/distribution/{Id}` |
|
|
| `ListDistributions` | GET | `/2020-05-31/distribution` |
|
|
| `AssociateAlias` | PUT | `/2020-05-31/distribution/{TargetDistributionId}/associate-alias` |
|
|
|
|
### Invalidations
|
|
|
|
| Operation | Method | Path |
|
|
|---|---|---|
|
|
| `CreateInvalidation` | POST | `/2020-05-31/distribution/{Id}/invalidation` |
|
|
| `GetInvalidation` | GET | `/2020-05-31/distribution/{Id}/invalidation/{InvId}` |
|
|
| `ListInvalidations` | GET | `/2020-05-31/distribution/{Id}/invalidation` |
|
|
|
|
### Cache Policies
|
|
|
|
| Operation | Method | Path |
|
|
|---|---|---|
|
|
| `CreateCachePolicy` | POST | `/2020-05-31/cache-policy` |
|
|
| `GetCachePolicy` | GET | `/2020-05-31/cache-policy/{Id}` |
|
|
| `GetCachePolicyConfig` | GET | `/2020-05-31/cache-policy/{Id}/config` |
|
|
| `UpdateCachePolicy` | PUT | `/2020-05-31/cache-policy/{Id}` |
|
|
| `DeleteCachePolicy` | DELETE | `/2020-05-31/cache-policy/{Id}` |
|
|
| `ListCachePolicies` | GET | `/2020-05-31/cache-policy` |
|
|
|
|
### Origin Request Policies
|
|
|
|
| Operation | Method | Path |
|
|
|---|---|---|
|
|
| `CreateOriginRequestPolicy` | POST | `/2020-05-31/origin-request-policy` |
|
|
| `GetOriginRequestPolicy` | GET | `/2020-05-31/origin-request-policy/{Id}` |
|
|
| `GetOriginRequestPolicyConfig` | GET | `/2020-05-31/origin-request-policy/{Id}/config` |
|
|
| `UpdateOriginRequestPolicy` | PUT | `/2020-05-31/origin-request-policy/{Id}` |
|
|
| `DeleteOriginRequestPolicy` | DELETE | `/2020-05-31/origin-request-policy/{Id}` |
|
|
| `ListOriginRequestPolicies` | GET | `/2020-05-31/origin-request-policy` |
|
|
|
|
### Response Headers Policies
|
|
|
|
| Operation | Method | Path |
|
|
|---|---|---|
|
|
| `CreateResponseHeadersPolicy` | POST | `/2020-05-31/response-headers-policy` |
|
|
| `GetResponseHeadersPolicy` | GET | `/2020-05-31/response-headers-policy/{Id}` |
|
|
| `GetResponseHeadersPolicyConfig` | GET | `/2020-05-31/response-headers-policy/{Id}/config` |
|
|
| `UpdateResponseHeadersPolicy` | PUT | `/2020-05-31/response-headers-policy/{Id}` |
|
|
| `DeleteResponseHeadersPolicy` | DELETE | `/2020-05-31/response-headers-policy/{Id}` |
|
|
| `ListResponseHeadersPolicies` | GET | `/2020-05-31/response-headers-policy` |
|
|
|
|
### Origin Access Control (OAC)
|
|
|
|
| Operation | Method | Path |
|
|
|---|---|---|
|
|
| `CreateOriginAccessControl` | POST | `/2020-05-31/origin-access-control` |
|
|
| `GetOriginAccessControl` | GET | `/2020-05-31/origin-access-control/{Id}` |
|
|
| `GetOriginAccessControlConfig` | GET | `/2020-05-31/origin-access-control/{Id}/config` |
|
|
| `UpdateOriginAccessControl` | PUT | `/2020-05-31/origin-access-control/{Id}` |
|
|
| `DeleteOriginAccessControl` | DELETE | `/2020-05-31/origin-access-control/{Id}` |
|
|
| `ListOriginAccessControls` | GET | `/2020-05-31/origin-access-control` |
|
|
|
|
### Origin Access Identity (OAI — legacy)
|
|
|
|
| Operation | Method | Path |
|
|
|---|---|---|
|
|
| `CreateCloudFrontOriginAccessIdentity` | POST | `/2020-05-31/origin-access-identity/cloudfront` |
|
|
| `GetCloudFrontOriginAccessIdentity` | GET | `/2020-05-31/origin-access-identity/cloudfront/{Id}` |
|
|
| `GetCloudFrontOriginAccessIdentityConfig` | GET | `/2020-05-31/origin-access-identity/cloudfront/{Id}/config` |
|
|
| `UpdateCloudFrontOriginAccessIdentity` | PUT | `/2020-05-31/origin-access-identity/cloudfront/{Id}/config` |
|
|
| `DeleteCloudFrontOriginAccessIdentity` | DELETE | `/2020-05-31/origin-access-identity/cloudfront/{Id}` |
|
|
| `ListCloudFrontOriginAccessIdentities` | GET | `/2020-05-31/origin-access-identity/cloudfront` |
|
|
|
|
### CloudFront Functions
|
|
|
|
| Operation | Method | Path |
|
|
|---|---|---|
|
|
| `CreateFunction` | POST | `/2020-05-31/function` |
|
|
| `GetFunction` | GET | `/2020-05-31/function/{Name}` |
|
|
| `DescribeFunction` | GET | `/2020-05-31/function/{Name}/describe` |
|
|
| `UpdateFunction` | PUT | `/2020-05-31/function/{Name}` |
|
|
| `PublishFunction` | POST | `/2020-05-31/function/{Name}/publish` |
|
|
| `DeleteFunction` | DELETE | `/2020-05-31/function/{Name}` |
|
|
| `ListFunctions` | GET | `/2020-05-31/function` |
|
|
|
|
### Public Keys and Key Groups
|
|
|
|
| Operation | Method | Path |
|
|
|---|---|---|
|
|
| `CreatePublicKey` | POST | `/2020-05-31/public-key` |
|
|
| `GetPublicKey` | GET | `/2020-05-31/public-key/{Id}` |
|
|
| `GetPublicKeyConfig` | GET | `/2020-05-31/public-key/{Id}/config` |
|
|
| `UpdatePublicKey` | PUT | `/2020-05-31/public-key/{Id}/config` |
|
|
| `DeletePublicKey` | DELETE | `/2020-05-31/public-key/{Id}` |
|
|
| `ListPublicKeys` | GET | `/2020-05-31/public-key` |
|
|
| `CreateKeyGroup` | POST | `/2020-05-31/key-group` |
|
|
| `GetKeyGroup` | GET | `/2020-05-31/key-group/{Id}` |
|
|
| `GetKeyGroupConfig` | GET | `/2020-05-31/key-group/{Id}/config` |
|
|
| `UpdateKeyGroup` | PUT | `/2020-05-31/key-group/{Id}` |
|
|
| `DeleteKeyGroup` | DELETE | `/2020-05-31/key-group/{Id}` |
|
|
| `ListKeyGroups` | GET | `/2020-05-31/key-group` |
|
|
|
|
### Tagging
|
|
|
|
| Operation | Method | Path |
|
|
|---|---|---|
|
|
| `ListTagsForResource` | GET | `/2020-05-31/tagging?Resource={arn}` |
|
|
| `TagResource` | POST | `/2020-05-31/tagging?Operation=Tag&Resource={arn}` |
|
|
| `UntagResource` | POST | `/2020-05-31/tagging?Operation=Untag&Resource={arn}` |
|
|
|
|
## Behavior
|
|
|
|
- All distributions are immediately set to `Deployed` state (no async `InProgress` delay).
|
|
- Distribution IDs are 14 uppercase alphanumeric characters starting with `E` (e.g. `E1Z2X3C4V5B6N7`).
|
|
- Distribution domain names follow the pattern `{id}.cloudfront.net`, with the id lower-cased as AWS
|
|
writes it in a host name.
|
|
- Public key IDs are `K` followed by 13 uppercase alphanumeric characters (e.g. `K2JCJMDEHXQW5F`),
|
|
the value a signed URL carries as `Key-Pair-Id`. Key group, cache policy, origin request policy and
|
|
response headers policy IDs are UUIDs, as they are on AWS.
|
|
- ARNs are global — no region segment: `arn:aws:cloudfront::{accountId}:distribution/{id}`.
|
|
- Invalidations are immediately marked `Completed`.
|
|
- `DeleteDistribution` returns `DistributionNotDisabled` (409) if `Enabled` is `true` in the config.
|
|
- All mutating operations (`PUT`, `DELETE`) require an `If-Match` header containing the current
|
|
`ETag`. Response headers policies, public keys, and key groups distinguish a missing header
|
|
(`InvalidIfMatchVersion`, 400) from a stale `ETag` (`PreconditionFailed`, 412). Other CloudFront
|
|
resources currently return `InvalidIfMatchVersion` (400) for either case.
|
|
- All `GET` and `POST` (create) responses include an `ETag` response header.
|
|
- List operations emit the payload root declared by the CloudFront REST XML model (for example,
|
|
`ListDistributions` returns `<DistributionList>`), with list contents represented by
|
|
`<Quantity>N</Quantity><Items>...</Items>`.
|
|
- OAI `CallerReference` uniqueness is enforced — duplicate `CallerReference` values return `CloudFrontOriginAccessIdentityAlreadyExists` (409).
|
|
- CNAME aliases are globally unique. `AssociateAlias` atomically transfers an alias from its current
|
|
owner to the target distribution. Exact aliases take precedence over the most-specific matching
|
|
wildcard alias.
|
|
- Viewer GET/HEAD requests, and OPTIONS requests allowed by the matched cache behavior, addressed to
|
|
an enabled distribution's generated domain or alias are routed to the matching S3 or custom
|
|
origin. Origin forwarding preserves the raw path; custom-origin redirects are not followed.
|
|
- Every distribution is also served as `{id}.cloudfront.{host}` for each endpoint host Floci
|
|
resolves: `localhost`, `localhost.floci.io`, `localhost.localstack.cloud`, `FLOCI_HOSTNAME` and
|
|
every `FLOCI_DNS_EXTRA_SUFFIXES` entry. `{id}.cloudfront.localhost.floci.io` and
|
|
`{id}.cloudfront.localhost` reach loopback with no host-file edit and are covered by the generated
|
|
HTTPS certificate, so a signed URL for either can be downloaded over `https://`. See
|
|
[Downloading over HTTPS](#downloading-over-https).
|
|
- Origin custom headers are persisted through the CloudFront API and CloudFormation. They replace
|
|
same-named viewer headers on custom-origin GET/HEAD/OPTIONS requests. For in-process S3 origins, a
|
|
configured `Origin` header is used for S3 CORS evaluation. AWS-prohibited names, malformed
|
|
values, inconsistent quantities, duplicates, and quota violations are rejected with modeled
|
|
CloudFront errors when the distribution is created or updated.
|
|
- Cache behaviors with enabled `TrustedKeyGroups` require a valid CloudFront signed URL or signed
|
|
cookie before the origin is contacted. Signed URL parameters take precedence over signed cookies.
|
|
Canned and custom policies support SHA-1 or SHA-256 signatures with RSA-2048 or ECDSA P-256 public
|
|
keys. Custom policies enforce resource wildcards, expiration, optional activation time, and
|
|
IPv4 CIDR restrictions. Canned resources compare literally, including query strings. Exact custom
|
|
resources can include one raw query delimiter. As a conservative limitation, other custom
|
|
resources containing a raw `?` fail closed because the character is ambiguous with CloudFront's
|
|
one-character wildcard; custom query-string wildcards are therefore not supported. Invalid or
|
|
expired signatures return 403.
|
|
- A key group must contain one to five existing public keys. Public keys that belong to a key group
|
|
and key groups referenced by a cache behavior cannot be deleted until those references are removed.
|
|
- Application query parameters are retained when constructing the resource covered by a signature.
|
|
CloudFront signing parameters are excluded from that resource and are never sent to the origin.
|
|
- S3-origin reads honor anonymous access, OAI bucket-policy or object-ACL grants, and OAC
|
|
service-principal bucket-policy grants (including the distribution `AWS:SourceArn`) when strict S3
|
|
authentication is enabled. OAC `always`, `never`, and unsigned `no-override` requests follow their
|
|
documented signing behavior; signed `no-override` viewer requests retain their authorization.
|
|
- Cache-policy, origin-request-policy, and legacy `ForwardedValues` data-plane evaluation is not
|
|
implemented yet. Viewer query strings therefore follow CloudFront's default behavior and are not
|
|
forwarded to origins.
|
|
- Custom origins that resolve to loopback, private, link-local, carrier-grade NAT, or other non-routable addresses are rejected by default. Development-only private origins must be explicitly allowlisted by exact hostname.
|
|
- Response headers policies validate the AWS configuration shape and are applied after the origin
|
|
response, including CORS preflight fields, origin override behavior, custom headers, security
|
|
headers, allowed header removals, and sampled `Server-Timing` metrics. `Pragma: server-timing`
|
|
forces those metrics for enabled policies. Distribution writes reject unknown policy IDs, and
|
|
policies attached to a cache behavior cannot be deleted.
|
|
- Up to 20 custom response headers policies can be created, and one policy can be associated with
|
|
up to 100 distributions.
|
|
- The five AWS managed response headers policy IDs are available and can be selected with
|
|
`ListResponseHeadersPolicies?Type=managed`; `Type` uses the AWS lowercase `managed` or `custom`
|
|
values.
|
|
|
|
## Configuration
|
|
|
|
| Property | Env var | Default | Description |
|
|
|---|---|---|---|
|
|
| `floci.services.cloudfront.enabled` | `FLOCI_SERVICES_CLOUDFRONT_ENABLED` | `true` | Enable or disable the service |
|
|
| `floci.services.cloudfront.domain-suffix` | `FLOCI_SERVICES_CLOUDFRONT_DOMAIN_SUFFIX` | `cloudfront.net` | Domain suffix for generated distribution domain names |
|
|
| `floci.services.cloudfront.allowed-private-origin-hosts` | `FLOCI_SERVICES_CLOUDFRONT_ALLOWED_PRIVATE_ORIGIN_HOSTS` | `[]` | Exact custom-origin hosts permitted to resolve to private/non-routable addresses (comma-separated in the environment variable) |
|
|
|
|
## CLI Examples
|
|
|
|
```bash
|
|
export AWS_ENDPOINT_URL=http://localhost:4566
|
|
export AWS_DEFAULT_REGION=us-east-1
|
|
export AWS_ACCESS_KEY_ID=test
|
|
export AWS_SECRET_ACCESS_KEY=test
|
|
|
|
# Create a distribution with an S3 origin
|
|
aws cloudfront create-distribution --distribution-config '{
|
|
"CallerReference": "ref-1",
|
|
"Enabled": true,
|
|
"Comment": "my distribution",
|
|
"Origins": {
|
|
"Quantity": 1,
|
|
"Items": [{
|
|
"Id": "my-origin",
|
|
"DomainName": "mybucket.s3.amazonaws.com",
|
|
"S3OriginConfig": {"OriginAccessIdentity": ""}
|
|
}]
|
|
},
|
|
"DefaultCacheBehavior": {
|
|
"TargetOriginId": "my-origin",
|
|
"ViewerProtocolPolicy": "redirect-to-https",
|
|
"CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6",
|
|
"AllowedMethods": {"Quantity": 2, "Items": ["GET","HEAD"]},
|
|
"Compress": true
|
|
}
|
|
}'
|
|
|
|
# Get a distribution
|
|
aws cloudfront get-distribution --id E1Z2X3C4V5B6N7
|
|
|
|
# List distributions
|
|
aws cloudfront list-distributions
|
|
|
|
# Create a cache invalidation
|
|
aws cloudfront create-invalidation \
|
|
--distribution-id E1Z2X3C4V5B6N7 \
|
|
--invalidation-batch '{
|
|
"CallerReference": "inv-1",
|
|
"Paths": {"Quantity": 1, "Items": ["/*"]}
|
|
}'
|
|
|
|
# Create an OAI (Origin Access Identity)
|
|
aws cloudfront create-cloud-front-origin-access-identity \
|
|
--cloud-front-origin-access-identity-config \
|
|
"CallerReference=oai-1,Comment=my-oai"
|
|
|
|
# Create an OAC (Origin Access Control)
|
|
aws cloudfront create-origin-access-control \
|
|
--origin-access-control-config '{
|
|
"Name": "my-oac",
|
|
"Description": "",
|
|
"OriginAccessControlOriginType": "s3",
|
|
"SigningBehavior": "always",
|
|
"SigningProtocol": "sigv4"
|
|
}'
|
|
|
|
# Create a cache policy
|
|
aws cloudfront create-cache-policy --cache-policy-config '{
|
|
"Name": "my-cache-policy",
|
|
"DefaultTTL": 86400,
|
|
"MinTTL": 0,
|
|
"MaxTTL": 31536000,
|
|
"ParametersInCacheKeyAndForwardedToOrigin": {
|
|
"EnableAcceptEncodingGzip": true,
|
|
"EnableAcceptEncodingBrotli": true,
|
|
"HeadersConfig": {"HeaderBehavior": "none"},
|
|
"CookiesConfig": {"CookieBehavior": "none"},
|
|
"QueryStringsConfig": {"QueryStringBehavior": "none"}
|
|
}
|
|
}'
|
|
|
|
# Disable and delete a distribution
|
|
ETAG=$(aws cloudfront get-distribution --id E1Z2X3C4V5B6N7 \
|
|
--query 'ETag' --output text)
|
|
aws cloudfront update-distribution --id E1Z2X3C4V5B6N7 \
|
|
--if-match "$ETAG" \
|
|
--distribution-config '...(config with Enabled: false)...'
|
|
ETAG=$(aws cloudfront get-distribution --id E1Z2X3C4V5B6N7 \
|
|
--query 'ETag' --output text)
|
|
aws cloudfront delete-distribution --id E1Z2X3C4V5B6N7 --if-match "$ETAG"
|
|
```
|
|
|
|
## Downloading over HTTPS
|
|
|
|
A distribution's own domain name (`{id}.cloudfront.net` by default) resolves to nothing local, so
|
|
address the distribution by one of its local delivery hostnames instead. `*.cloudfront.localhost.floci.io`
|
|
resolves to `127.0.0.1` through public DNS and works on Linux, macOS and in containers.
|
|
`*.cloudfront.localhost` needs no DNS at all but only resolves where the runtime handles `.localhost`
|
|
itself, which macOS and browsers do and Debian-based images do not.
|
|
|
|
Start Floci with [TLS](../configuration/tls.md) enabled and trust its CA once:
|
|
|
|
```bash
|
|
docker run -e FLOCI_TLS_ENABLED=true -p 4566:4566 floci/floci:latest
|
|
curl -s http://localhost:4566/_floci/ca.pem -o floci-root-ca.pem
|
|
```
|
|
|
|
Sign the URL of the hostname you download from — the port is part of what a canned policy signs —
|
|
and fetch it:
|
|
|
|
```bash
|
|
HOST=e1z2x3c4v5b6n7.cloudfront.localhost.floci.io:4566
|
|
|
|
SIGNED=$(aws cloudfront sign \
|
|
--url "https://$HOST/hello.txt" \
|
|
--key-pair-id K2JCJMDEHXQW5F \
|
|
--private-key file://private_key.pem \
|
|
--date-less-than 2026-12-31T00:00:00Z)
|
|
|
|
curl --cacert floci-root-ca.pem "$SIGNED"
|
|
```
|
|
|
|
To drop the `:4566`, publish the HTTPS port Floci also binds when TLS is on (`-p 443:443`, see
|
|
`FLOCI_TLS_AWS_HTTPS_PORT`) and sign `https://e1z2x3c4v5b6n7.cloudfront.localhost.floci.io/hello.txt`.
|
|
|
|
Set `FLOCI_SERVICES_CLOUDFRONT_DOMAIN_SUFFIX=cloudfront.localhost.floci.io` to have
|
|
`CreateDistribution` return that hostname as the `DomainName`, so test code can sign the API response
|
|
as it is.
|
|
|
|
## Not Supported (Phase 2)
|
|
|
|
- Continuous deployment policies (`CreateContinuousDeploymentPolicy`, etc.)
|
|
- `CopyDistribution` (staging distributions)
|
|
- Real-time log configs (`CreateRealtimeLogConfig`, etc.)
|
|
- Field-level encryption (`CreateFieldLevelEncryptionConfig`, etc.)
|
|
- `TestFunction` execution (function is stored, not executed)
|
|
- Streaming distributions (RTMP — deprecated by AWS)
|
|
- VPC origins, Anycast IP lists, key value stores
|
|
- Monitoring subscriptions
|
|
- CloudFormation provisioning of custom `AWS::CloudFront::ResponseHeadersPolicy` resources
|
|
(literal custom or managed policy IDs are supported on distributions)
|
|
- Persistent edge caching and global CDN propagation
|