Пример создания хостинга статического сайта в AWS S3 и подключении к нему AWS CloudFront CDN, к которому подключается SSL сертификат из AWS ACM.
Для сайта используем домен site.azinchenko.com.
Получившийся шаблон доступен в репозитории тут>>>.
Содержание
AWS::S3::Bucket
Начнём с создания корзины, используем ресурс AWS::S3::Bucket
:
{ "AWSTemplateFormatVersion": "2010-09-09", "Description": "AWS CloudFormation S3 website hosting with CloudFront CND stack", "Parameters": { "S3SiteBucketName": { "Description": "S3 website hosting bucket name", "Type": "String", "Default": "site.azinchenko.com" } }, "Resources": { "SiteBucket": { "Type": "AWS::S3::Bucket", "DeletionPolicy": "Delete", "Properties": { "BucketName": { "Ref": "S3SiteBucketName" }, "WebsiteConfiguration": { "IndexDocument": "index.html", "ErrorDocument": "404.html" } } } } }
Тут в DeletionPolicy
указываем удаление корзины при удалении стека, а в WebsiteConfiguration
– указываем на то, что корзина будет использоваться для хостинга сайта.
В имени корзины используем имя домена, которое будет использоваться для доступа к сайту – site.azinchenko.com.
Создаём стек:
[simterm]
$ aws --profile setevoy-root cloudformation create-stack --stack-name s3-cdn-site-example --template-body file://s3_website_hostig_with_cloudfront.json
[/simterm]
Проверяем корзину:
[simterm]
$ aws --profile setevoy-root s3api list-buckets | jq '.Buckets[] | select(.Name=="site.azinchenko.com")' { "Name": "site.azinchenko.com", "CreationDate": "2018-09-05T07:33:26.000Z" }
[/simterm]
Создаём в ней индексный файл:
[simterm]
$ echo "This is The Website" > /tmp/index.html $ aws --profile setevoy-root s3 cp /tmp/index.html s3://site.azinchenko.com upload: ../../../../../../tmp/index.html to s3://site.azinchenko.com/index.html
[/simterm]
Проверяем файл:
[simterm]
$ aws --profile setevoy-root s3api list-objects --bucket site.azinchenko.com | jq '.Contents[] .Key' "index.html"
[/simterm]
Но если проверить доступ по HTTP сейчас – получим AccessDenied:
[simterm]
$ curl http://site.azinchenko.com.s3-website-eu-west-1.amazonaws.com <html> <head><title>403 Forbidden</title></head> <body> <h1>403 Forbidden</h1> <ul> <li>Code: AccessDenied</li> <li>Message: Access Denied</li> ...
[/simterm]
AWS::S3::BucketPolicy
Добавляем ещё один ресурс – AWS::S3::BucketPolicy
, в котором описываем доступ к корзине – разрешаем метод s3:GetObject
ко всем объектам в корзине, и делаем её таким образом Publicly accessible:
... "BucketPolicy": { "Type": "AWS::S3::BucketPolicy", "Properties": { "PolicyDocument": { "Id": "MyPolicy", "Version": "2012-10-17", "Statement": [{ "Sid": "PublicReadForGetBucketObjects", "Effect": "Allow", "Principal": "*", "Action": "s3:GetObject", "Resource": { "Fn::Join": ["", [ "arn:aws:s3:::", { "Ref": "SiteBucket" }, "/*" ]] } }] }, "Bucket": { "Ref": "SiteBucket" } } } ...
Обновляем стек:
[simterm]
$ aws --profile setevoy-root cloudformation update-stack --stack-name s3-cdn-site-example --template-body file://s3_website_hostig_with_cloudfront.json
Проверяем:
[simterm]
$ curl http://site.azinchenko.com.s3-website-eu-west-1.amazonaws.com This is The Website
[/simterm]
AWS Certificate Manager
Сертификат не будет являться частью стека, делаем его вручную, обязательно в регионе N. Virginia (us-east-1
), что бы его можно было использовать в CloudFront:
AWS::CloudFront::Distribution
И последним добавляем ресурс CDN distribution.
Из интересного в его параметрах:
TargetOriginId
– черезRef
указываем ID ресурса S3 корзиныViewerProtocolPolicy
– редиректим HTTP запросы на HTTPSAliases
– в алиасе (поле CNAME в CloudFront distribution) указываем DNS имя, которое используем для сайта, в этом примере это site.azinchenko.com, как и имя корзиныOrigins
– тут формируем URL корзины в виде имя-корзины-s3-website.eu-west-1.amazonaws.com, используяFn::Join
иRef
ViewerCertificate
– указываем SSL ARN, который создали выше
ARN сертификата выносим в Parameters
шаблона:
... "SiteSSLARN": { "Description": "site.azinchenko.com ACM SSL ARN", "Type": "String", "Default": "arn:aws:acm:us-east-1:264***286:certificate/601dae7d-***-d29307183727" } ...
И добавляем ресурс AWS::CloudFront::Distribution
:
... "CloudFrontCDN": { "Type": "AWS::CloudFront::Distribution", "Properties": { "DistributionConfig": { "Comment": "Example WebSite CDN", "Enabled": true, "DefaultRootObject" : "index.html", "DefaultCacheBehavior": { "TargetOriginId": { "Ref": "SiteBucket" }, "ViewerProtocolPolicy": "redirect-to-https", "MinTTL": 0, "AllowedMethods": [ "HEAD", "GET" ], "CachedMethods": [ "HEAD", "GET" ], "ForwardedValues": { "QueryString": false, "Cookies": { "Forward": "none" } } }, "Aliases" : [ {"Ref": "S3SiteBucketName"} ], "Origins": [ { "DomainName": { "Fn::Join": [ ".", [ {"Ref": "SiteBucket"}, "s3-website.eu-west-1.amazonaws.com" ] ] }, "Id": { "Ref": "SiteBucket" }, "CustomOriginConfig": { "HTTPPort": "80", "HTTPSPort": "443", "OriginProtocolPolicy": "http-only" } } ], "ViewerCertificate": { "SslSupportMethod": "sni-only", "MinimumProtocolVersion": "TLSv1", "AcmCertificateArn": { "Ref": "SiteSSLARN" } } }, "Tags" : [ {"Key" : "Name", "Value" : { "Fn::Join" : [ "-", [ {"Ref" : "AWS::StackName"}, "site-cdn"] ] } } ] } } ...
Обновляем стек:
[simterm]
$ aws --profile setevoy-root cloudformation update-stack --stack-name s3-cdn-site-example --template-body file://s3_website_hostig_with_cloudfront.json
[/simterm]
Проверяем:
[simterm]
$ aws --profile setevoy-root cloudfront list-distributions { "DistributionList": { "Items": [ { "Id": "E15M912IHI41HB", "ARN": "arn:aws:cloudfront::264***286:distribution/E15M912IHI41HB", "Status": "InProgress", "LastModifiedTime": "2018-09-05T08:34:33.994Z", "DomainName": "d16suo1j0j3qwj.cloudfront.net", "Aliases": { "Quantity": 1, "Items": [ "site.azinchenko.com" ] }, "Origins": { "Quantity": 1, "Items": [ { "Id": "site.azinchenko.com", "DomainName": "site.azinchenko.com.s3-website.eu-west-1.amazonaws.com", ...
[/simterm]
Ждём, когда дистрибьюция переходит в Status == Enabled, и переходим в DNS.
Route53
Последний шаг – обновить запись site.azinchenko.com в Route53.
Выбираем тип записи IN A, и указываем Alias – CloudFront distribution:
Ждём обновления DNS у провайдера:
[simterm]
$ dig +short site.azinchenko.com 143.204.10.118 143.204.10.134 143.204.10.200 143.204.10.37
[/simterm]
Проверяем:
[simterm]
$ curl -Lv site.azinchenko.com * Rebuilt URL to: site.azinchenko.com/ ... < HTTP/1.1 301 Moved Permanently < Server: CloudFront ... < Location: https://site.azinchenko.com/ ... * Issue another request to this URL: 'https://site.azinchenko.com/' ... * SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256 ... * Server certificate: * subject: CN=site.azinchenko.com ... < HTTP/1.1 200 OK ... This is The Website
[/simterm]
Готово.