Mike Slinn
Mike Slinn

AWS S3 and CloudFront SSL

Published 2021-03-19.
Time to read: about 2 minutes.

This article is categorized under AWS.

SSL certificates need to match the domain they are served from.

AWS uses one of several SSL certificates, depending on the domain that an asset is requested from.

  • AWS S3 applies an SSL certificate for https requests. The SSL certificate chosen depends on the bucket endpoint used: s3.amazonaws.com, *.s3.amazonaws.com, or s3.region.amazonaws.com.
  • AWS CloudFront will apply your custom SSL certificate (for example, a wildcard certificate such as *.ancientwarmth.com) for https requests to the CNAME for that distribution, otherwise it will apply the wildcard SSL certificate for *.cloudfront.net.

Example SSL URLs

My AWS S3 bucket called assets.ancientwarmth.com is served via a CloudFront distribution with URL d1bci9l8cjf24o.cloudfront.net that applies a wildcard SSL certificate for *.ancientwarmth.com that I created using AWS Certificate Manager. I defined a CNAME called assets.ancientwarmth.com for that same CloudFront distribution using Route 53.

All of the following URLs can be used to access my content, providing the SSL certificate matches the requested domain.

URL: https://d1bci9l8cjf24o.cloudfront.net
Origin Type: CloudFront distribution
SSL certificate origin: *.cloudfront.net
Valid SSL certificate? Yes.

URL: https://assets.ancientwarmth.com
Origin Type: CloudFront distribution
SSL certificate origin: *.ancientwarmth.com
Valid SSL certificate? Yes. (I created this wildcard certificate using Route 53.)

S3 path-style URL: https://s3.us-east-1.amazonaws.com/assets.ancientwarmth.com
Origin Type: S3 bucket
SSL certificate origin: s3.us-east-1.amazonaws.com
Valid SSL certificate? Yes.

S3 dot URL: https://assets.ancientwarmth.com.s3.amazonaws.com
Origin Type: S3 bucket
SSL certificate origin: *.s3.amazonaws.com
Valid SSL certificate? No, does not match URL (wildcards only match one subdomain).

S3 dot Region URL: https://assets.ancientwarmth.com.s3.us-east-1.amazonaws.com
Origin Type: S3 bucket
SSL certificate origin: s3.amazonaws.com
Valid SSL certificate? No, does not match URL.

Testing with curl

Curl is often used to test SSL requests. In the following curl commands, the -I option just fetches the headers, and the -v option provides verbose output. You can see the SSL certificate negotation.

Fetching an asset from a CloudFront distribution using the AWS *.cloudfront.net wildcard SSL certificate:

Shell
$ curl -Iv \
  https://d1bci9l8cjf24o.cloudfront.net/js/jquery.modal.min.js
*   Trying 52.85.149.22:443...
* TCP_NODELAY set
* Connected to d1bci9l8cjf24o.cloudfront.net (52.85.149.22) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256
* ALPN, server accepted to use h2
* Server certificate:
*  subject: CN=*.cloudfront.net
*  start date: Feb 22 00:00:00 2021 GMT
*  expire date: Feb 21 23:59:59 2022 GMT
*  subjectAltName: host "d1bci9l8cjf24o.cloudfront.net" matched cert's "*.cloudfront.net"
*  issuer: C=US; O=DigiCert Inc; CN=DigiCert Global CA G2
*  SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x5585dcfaf7e0)
> HEAD /js/jquery.modal.min.js HTTP/2
> Host: d1bci9l8cjf24o.cloudfront.net
> user-agent: curl/7.68.0
> accept: */*
>
* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
< HTTP/2 200
HTTP/2 200
< content-type: application/javascript
content-type: application/javascript
< content-length: 4953
content-length: 4953
< date: Sat, 20 Mar 2021 14:11:34 GMT
date: Sat, 20 Mar 2021 14:11:34 GMT
< last-modified: Sat, 20 Mar 2021 03:14:08 GMT
last-modified: Sat, 20 Mar 2021 03:14:08 GMT
< etag: "c8f50397e0560719c62a35318f413e16"
etag: "c8f50397e0560719c62a35318f413e16"
< accept-ranges: bytes
accept-ranges: bytes
< server: AmazonS3
server: AmazonS3
< x-cache: Miss from cloudfront
x-cache: Miss from cloudfront
< via: 1.1 0712e4ad4264127dfcb76a114b130495.cloudfront.net (CloudFront)
via: 1.1 0712e4ad4264127dfcb76a114b130495.cloudfront.net (CloudFront)
< x-amz-cf-pop: IAD89-C3
x-amz-cf-pop: IAD89-C3
< x-amz-cf-id: hWrjwajqqkI9-rJnK1BSQqkX9DPXIlZJLfa28UaIeze7taBP5kqMNg==
x-amz-cf-id: hWrjwajqqkI9-rJnK1BSQqkX9DPXIlZJLfa28UaIeze7taBP5kqMNg==

<
* Connection #0 to host d1bci9l8cjf24o.cloudfront.net left intact 

Fetching an asset from a CloudFront distribution using my *.ancientwarmth.com wildcard SSL certificate:

Shell
$ curl -Iv \
  https://assets.ancientwarmth.com/js/jquery.modal.min.js
modal.min.js>   https://assets.ancientwarmth.com/js/jquery.modal.min.js
  *   Trying 13.226.36.16:443...
  * TCP_NODELAY set
  * Connected to assets.ancientwarmth.com (13.226.36.16) port 443 (#0)
  * ALPN, offering h2
  * ALPN, offering http/1.1
  * successfully set certificate verify locations:
  *   CAfile: /etc/ssl/certs/ca-certificates.crt
    CApath: /etc/ssl/certs
  * TLSv1.3 (OUT), TLS handshake, Client hello (1):
  * TLSv1.3 (IN), TLS handshake, Server hello (2):
  * TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
  * TLSv1.3 (IN), TLS handshake, Certificate (11):
  * TLSv1.3 (IN), TLS handshake, CERT verify (15):
  * TLSv1.3 (IN), TLS handshake, Finished (20):
  * TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
  * TLSv1.3 (OUT), TLS handshake, Finished (20):
  * SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256
  * ALPN, server accepted to use h2
  * Server certificate:
  *  subject: CN=*.ancientwarmth.com
  *  start date: Mar 14 00:00:00 2021 GMT
  *  expire date: Apr 12 23:59:59 2022 GMT
  *  subjectAltName: host "assets.ancientwarmth.com" matched cert's "*.ancientwarmth.com"
  *  issuer: C=US; O=Amazon; OU=Server CA 1B; CN=Amazon
  *  SSL certificate verify ok.
  * Using HTTP2, server supports multi-use
  * Connection state changed (HTTP/2 confirmed)
  * Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
  * Using Stream ID: 1 (easy handle 0x5599720157e0)
  > GET /js/jquery.modal.min.js HTTP/2
  > Host: assets.ancientwarmth.com
  > user-agent: curl/7.68.0
  > accept: */*
  >
  * Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
  < HTTP/2 200
  < content-type: application/javascript
  < content-length: 4953
  < date: Sat, 20 Mar 2021 12:34:25 GMT
  < last-modified: Sat, 20 Mar 2021 03:14:08 GMT
  < etag: "c8f50397e0560719c62a35318f413e16"
  < accept-ranges: bytes
  < server: AmazonS3
  < x-cache: Hit from cloudfront
  < via: 1.1 4e3df844337032b56b8434990b0f76ca.cloudfront.net (CloudFront)
  < x-amz-cf-pop: EWR53-C2
  < x-amz-cf-id: 17Dxn6QqtK6JfkJwFnESVYsG-Cbzu6H-sOTWcGDpznGcpjIZbhJDRA==
  < age: 5195

  <
* Connection #0 to host assets.ancientwarmth.com left intact 

Fetching an asset from an S3 bucket using an AWS SSL certificate for all S3 buckets in region us-east-1:

Shell
$ curl -Iv \
  https://s3.us-east-1.amazonaws.com/assets.ancientwarmth.com/js/jquery.modal.min.js
*   Trying 52.216.24.46:443...
* TCP_NODELAY set
* Connected to s3.us-east-1.amazonaws.com (52.216.24.46) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: C=US; ST=Washington; L=Seattle; O=Amazon.com, Inc.; CN=s3.amazonaws.com
*  start date: Aug  4 00:00:00 2020 GMT
*  expire date: Aug  9 12:00:00 2021 GMT
*  subjectAltName: host "s3.us-east-1.amazonaws.com" matched cert's "s3.us-east-1.amazonaws.com"
*  issuer: C=US; O=DigiCert Inc; OU=www.digicert.com; CN=DigiCert Baltimore CA-2 G2
*  SSL certificate verify ok.
> GET /assets.ancientwarmth.com/js/jquery.modal.min.js HTTP/1.1
> Host: s3.us-east-1.amazonaws.com
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< x-amz-id-2: xIXUHy7YBpjZaF+cpGoSAwNvC5+NrmM5pmJM8nInI6weEkbht350xSPC9+yOBJrGs9GY0hn2V7Y=
< x-amz-request-id: JM2K8HR109JNMMB1
< Date: Sat, 20 Mar 2021 14:03:56 GMT
< Last-Modified: Sat, 20 Mar 2021 03:14:08 GMT
< ETag: "c8f50397e0560719c62a35318f413e16"
< Accept-Ranges: bytes
< Content-Type: application/javascript
< Content-Length: 4953
< Server: AmazonS3

<
* Connection #0 to host s3.us-east-1.amazonaws.com left intact